【问题标题】:Python YAML to JSON to YAMLPython YAML 到 JSON 到 YAML
【发布时间】:2019-01-25 14:59:43
【问题描述】:

我是 python 新手,所以我正在构建一个简单的程序来将 YAML 解析为 JSON 并将 JSON 解析为 YAML。

yaml2json 在一行中将 YAML 转换为 JSON,但 JSON 验证器说它是正确的。

这是我目前的代码:

def parseyaml(inFileType, outFileType):
   infile = input('Please enter a {} filename to parse: '.format(inFileType))
   outfile = input('Please enter a {} filename to output: '.format(outFileType))

   with open(infile, 'r') as stream:
       try:
           datamap = yaml.safe_load(stream)
           with open(outfile, 'w') as output:
               json.dump(datamap, output)
       except yaml.YAMLError as exc:
           print(exc)

    print('Your file has been parsed.\n\n')


def parsejson(inFileType, outFileType):
   infile = input('Please enter a {} filename to parse: '.format(inFileType))
   outfile = input('Please enter a {} filename to output: '.format(outFileType))

   with open(infile, 'r') as stream:
       try:
           datamap = json.load(stream)
           with open(outfile, 'w') as output:
               yaml.dump(datamap, output)
       except yaml.YAMLError as exc:
           print(exc)

   print('Your file has been parsed.\n\n')

原始 YAML 与新 YAML 的示例

原文:

inputs:
  webTierCpu:
    type: integer
    minimum: 2
    default: 2
    maximum: 5
    title: Web Server CPU Count
    description: The number of CPUs for the Web nodes

新:

inputs:
  dbTierCpu: {default: 2, description: The number of CPUs for the DB node, maximum: 5,
    minimum: 2, title: DB Server CPU Count, type: integer}

它看起来不像是解码所有 JSON,所以我不确定下一步应该去哪里......

【问题讨论】:

  • 解析后的输出看起来包含所有相同的数据(除了标题和描述中的不同文本,只是包含在大括号内且顺序不同。
  • YAML 是 JSON 的超集。或者换句话说:有效的 JSON 也是有效的 YAML。 (不是相反)
  • 您无法重新复制原始 YAML,因为在转换为 JSON 时会丢失换行符和顺序。
  • 在您的parseyaml 中,except 之前存在缩进错误。
  • @Daniel 我不确定您所说的“换行符 [...] 迷路”是什么意思,但无论如何您的结论似乎都是无效的。中间 JSON 流当然有一个明确的顺序,就像 YAML 源一样,并且在保留该顺序的同时将具有明确顺序的 Mapping 转储到 JSON 是相对简单的。加载 JSON 保留顺序更加简单。

标签: python json yaml


【解决方案1】:
function yaml_validate {
  python -c 'import sys, yaml, json; yaml.safe_load(sys.stdin.read())'
}

function yaml2json {
  python -c 'import sys, yaml, json; print(json.dumps(yaml.safe_load(sys.stdin.read())))'
}

function yaml2json_pretty {
  python -c 'import sys, yaml, json; print(json.dumps(yaml.safe_load(sys.stdin.read()), indent=2, sort_keys=False))'
}

function json_validate {
  python -c 'import sys, yaml, json; json.loads(sys.stdin.read())'
}

function json2yaml {
  python -c 'import sys, yaml, json; print(yaml.dump(json.loads(sys.stdin.read())))'
}

更多有用的 Bash 技巧在 http://github.com/frgomes/bash-scripts

【讨论】:

    【解决方案2】:

    您的文件正在丢失其格式,因为原来的 dump 例程 默认情况下以 YAML 流样式写入所有叶节点,而您的输入是块样式 一路走来。

    您还丢失了键的顺序,首先是因为 JSON 解析器 使用 dict,其次是因为 dump 对输出进行排序。

    如果您查看中间 JSON,您已经看到关键顺序是 在那一点上消失了。要保留它,请使用新的 API 加载您的 YAML 并有一个特殊的 JSON 编码器作为转储的替代品,可以 处理加载 YAML 的 Mapping 的子类,类似于 this 例子 来自标准 Python 文档。

    假设您的 YAML 存储在 input.yaml

    import sys
    import json
    from collections.abc import Mapping, Sequence
    from collections import OrderedDict
    import ruamel.yaml
    
    # if you instantiate a YAML instance as yaml, you have to explicitly import the error
    from ruamel.yaml.error import YAMLError
    
    
    yaml = ruamel.yaml.YAML()  # this uses the new API
    # if you have standard indentation, no need to use the following
    yaml.indent(sequence=4, offset=2)
    
    input_file = 'input.yaml'
    intermediate_file = 'intermediate.json'
    output_file = 'output.yaml'
    
    
    class OrderlyJSONEncoder(json.JSONEncoder):
        def default(self, o):
            if isinstance(o, Mapping):
                return OrderedDict(o)
            elif isinstance(o, Sequence):
                return list(o)
            return json.JSONEncoder.default(self, o)
    
    
    def yaml_2_json(in_file, out_file):
        with open(in_file, 'r') as stream:
            try:
                datamap = yaml.load(stream)
                with open(out_file, 'w') as output:
                    output.write(OrderlyJSONEncoder(indent=2).encode(datamap))
            except YAMLError as exc:
                print(exc)
                return False
        return True
    
    
    yaml_2_json(input_file, intermediate_file)
    with open(intermediate_file) as fp:
        sys.stdout.write(fp.read())
    

    给出:

    {
      "inputs": {
        "webTierCpu": {
          "type": "integer",
          "minimum": 2,
          "default": 2,
          "maximum": 5,
          "title": "Web Server CPU Count",
          "description": "The number of CPUs for the Web nodes"
        }
      }
    }
    

    您看到您的 JSON 具有适当的键顺序,我们也 需要在加载时保存。你可以在没有子类化的情况下做到这一点 任何东西,通过指定将 JSON objects 加载到 Mapping,YAML 解析器在内部使用,通过提供object_pairs_hook

    from ruamel.yaml.comments import CommentedMap
    
    
    def json_2_yaml(in_file, out_file):
        with open(in_file, 'r') as stream:
            try:
                datamap = json.load(stream, object_pairs_hook=CommentedMap)
                # if you need to "restore" literal style scalars, etc.
                # walk_tree(datamap)
                with open(out_file, 'w') as output:
                    yaml.dump(datamap, output)
            except yaml.YAMLError as exc:
                print(exc)
                return False
        return True
    
    
    json_2_yaml(intermediate_file, output_file)
    with open(output_file) as fp:
        sys.stdout.write(fp.read())
    

    哪些输出:

    inputs:
      webTierCpu:
        type: integer
        minimum: 2
        default: 2
        maximum: 5
        title: Web Server CPU Count
        description: The number of CPUs for the Web nodes
    

    我希望这与您的原始输入足够相似以被接受。

    注意事项:

    • 在使用新的 API 时,我倾向于使用 yaml 作为 ruamel.yaml.YAML() 的实例,而不是 from ruamel import yaml。然而,这掩盖了yaml.YAMLError 的使用,因为 错误类不是YAML()的属性

    • 如果你正在开发这种东西,我建议你删除 至少来自实际功能的用户输入。它应该是 写你的parseyamlparsejson 打电话给yaml_2_json 是微不足道的。 json_2_yaml

    • 原始 YAML 文件中的所有 cmets 都将丢失,尽管 ruamel.yaml 可以加载它们。 JSON 最初确实允许 cmets,但它是 不在规范中,而且我知道没有解析器可以输出 cmets。


    由于你的真实文件有文字块标量,你必须使用一些魔法来取回它们。

    包括以下函数,它们遍历树,递归到 dict 值和列表元素,并将任何带有嵌入换行符的行转换为一种类型,该类型以文本块样式标量的形式输出到 YAML(因此没有返回值):

    from ruamel.yaml.scalarstring import PreservedScalarString, SingleQuotedScalarString
    from ruamel.yaml.compat import string_types, MutableMapping, MutableSequence
    
    def preserve_literal(s):
        return PreservedScalarString(s.replace('\r\n', '\n').replace('\r', '\n'))
    
    def walk_tree(base):
        if isinstance(base, MutableMapping):
            for k in base:
                v = base[k]  # type: Text
                if isinstance(v, string_types):
                    if '\n' in v:
                        base[k] = preserve_literal(v)
                    elif '${' in v or ':' in v:
                        base[k] = SingleQuotedScalarString(v)
                else:
                    walk_tree(v)
        elif isinstance(base, MutableSequence):
            for idx, elem in enumerate(base):
                if isinstance(elem, string_types):
                    if '\n' in elem:
                        base[idx] = preserve_literal(elem)
                    elif '${' in elem or ':' in elem:
                        base[idx] = SingleQuotedScalarString(elem)
                else:
                    walk_tree(elem)
    

    然后做

        walk_tree(datamap)
    

    从 JSON 加载数据后。

    根据以上所有内容,您的Wordpress.yaml 文件中应该只有一行不同。

    【讨论】:

    • 当然可以使用 YAML 解析器直接加载 JSON,因为 ruamel.yaml 实现了 YAML 1.2 规范。然后您只需添加yaml.default_flow_style = False 即可获得块样式输出,因为JSON 输入始终是流样式。不过,您可能会考虑这样做“作弊”。
    • 太棒了!我有一个问题。我正在使用它将配置传递给图像上的 cloudconfig。这是原创的。云配置:| #cloud-config 主机名:wordpress-web chpasswd:列表:| ubuntu:VMware1! expire: False 这是新的转换。 cloudConfig: "#cloud-config\nhostname: wordpress-web\nchpasswd:\n list: |\n\ \ ubuntu:VMware1!\n expire: False\nusers:\n - name: ubuntu\n passwd:\ \ \ "$1$Lg35t2vk$MIw6mB5VSuZsztttRGlJe0\"\n lock-passwd: False\n
    • 非常感谢您的帮助。我似乎没有收到用于转换的打印消息。我用完整的代码更新了 github。我想我可能放错了地方,但我不确定。
    • 那是用于调试的是 walk_tree 没有正确调用该函数(最初)。可以/应该被删除,我错过了,因为我得赶紧做晚饭。
    • 代码应该调用你刚刚定义的walk_tree(不是ruamel.yaml中的那个,这不起作用)并且你的输入文件和输出之间存在一些缩进和引用差异.我再次更新了答案,通过答案下方的编辑链接查看edit revisions。应该是这样的。下次请发布您的真实输入,或链接以及您已经做过的小样本。
    猜你喜欢
    • 2014-10-27
    • 1970-01-01
    • 2018-12-26
    • 2017-07-02
    • 2016-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-15
    相关资源
    最近更新 更多