【问题标题】:Python "yaml" module converting unexpected YAML from JSON formatPython“yaml”模块将意外的 YAML 从 JSON 格式转换
【发布时间】:2019-01-18 12:04:32
【问题描述】:

我正在尝试将 JSON 数据转换为 YAML 格式,但得到了意外的 YAML 输出

使用在线工具将 JSON 转换为 YAML,从而提供预期的 YAML 输出。但是,当在下面的 Python 代码中使用相同的 JSON 时,会得到意想不到的不同结果。

import yaml                                                                     

job_template = [                                                                
  {                                                                             
    "job-template": {                                                           
      "name": "{name}_job",                                                     
      "description": "job description",                                         
      "project-type": "multibranch",                                            
      "number-to-keep": 30,                                                     
      "days-to-keep": 30,                                                       
      "scm": [                                                                  
        {                                                                       
          "git": {                                                              
            "url": "{git_url}"                                                  
          }                                                                     
        }                                                                       
      ]                                                                         
    }                                                                           
  }                                                                             
]                                                                               

yaml.dump(job_template, open("job_template.yaml", "w"))   

期望低于 YAML 数据:

- job-template:
    name: "{name}_job"
    description: job description
    project-type: multibranch
    number-to-keep: 30
    days-to-keep: 30
    scm:
    - git:
        url: "{git_url}"

低于 YAML 格式:

 - job-template:
     days-to-keep: 30
     description: job description
     name: '{name}_job'
     number-to-keep: 30
     project-type: multibranch
     scm:
     - git: {url: '{git_url}'}

【问题讨论】:

  • 这个结果到底怎么不正确?它包含所有信息,并且是正确的 yaml 格式。
  • @brunodesthuilliers 结果不是错误的,是出乎意料的。

标签: python json yaml


【解决方案1】:

使用default_flow_style=False

例如:

import yaml                                                                     

job_template = [                                                                
  {                                                                             
    "job-template": {                                                           
      "name": "{name}_job",                                                     
      "description": "job description",                                         
      "project-type": "multibranch",                                            
      "number-to-keep": 30,                                                     
      "days-to-keep": 30,                                                       
      "scm": [                                                                  
        {                                                                       
          "git": {                                                              
            "url": "{git_url}"                                                  
          }                                                                     
        }                                                                       
      ]                                                                         
    }                                                                           
  }                                                                             
]                                                                               

yaml.dump(job_template, open("job_template.yaml", "w"), default_flow_style=False)  

【讨论】:

    【解决方案2】:

    问题出在 Python 代码中:dict 是一个无序容器。 pprint 只是给出你的 yaml 输出的相同顺序:

    >>> pprint.pprint(job_template)
    [{'job-template': {'days-to-keep': 30,
                       'description': 'job description',
                       'name': '{name}_job',
                       'number-to-keep': 30,
                       'project-type': 'multibranch',
                       'scm': [{'git': {'url': '{git_url}'}}]}}]
    

    如果问题是关于最后一级字典{"url": "{git_url}"}的表示样式,答案已由@Rakesh给出

    【讨论】:

      【解决方案3】:

      PyYAML 中排序的更改是对 YAML 文件的往返编辑的障碍,许多其他解析器已试图解决此问题。

      值得一看的是Ruamel.yaml,上面写着overview page

      block style and key ordering are kept, so you can diff the round-tripped source
      

      作者提供的代码示例演示了这一点:

      import sys
      import ruamel.yaml as yaml
      
      yaml_str = """\
      3: abc
      conf:
          10: def
          3: gij     # h is missing
      more:
      - what
      - else
      """
      
      data = yaml.load(yaml_str, Loader=yaml.RoundTripLoader)
      data['conf'][10] = 'klm'
      data['conf'][3] = 'jig'
      yaml.dump(data, sys.stdout, Dumper=yaml.RoundTripDumper)
      will give you:
      
      3: abc
      conf:
        10: klm
        3: jig       # h is missing
      more:
      - what
      - else
      

      这是更充分的讨论here。它被描述为 PyYAML 的直接替代品,因此应该很容易在您的环境中进行试验。

      【讨论】:

        【解决方案4】:

        首先,您应该将作业模板保留在 JSON 文件中,例如 input.json.:

        [                                                                
          {                                                                             
            "job-template": {                                                           
              "name": "{name}_job",                                                     
              "description": "job description",                                         
              "project-type": "multibranch",                                            
              "number-to-keep": 30,                                                     
              "days-to-keep": 30,                                                       
              "scm": [                                                                  
                {                                                                       
                  "git": {                                                              
                    "url": "{git_url}"                                                  
                  }                                                                     
                }                                                                       
              ]                                                                         
            }                                                                           
          }                                                                             
        ]
        

        这样您就可以更轻松地调整脚本以处理不同的文件。这样做 还保证您的 JSON 对象中的键是有序的,当您在代码中包含 JSON 作为 dicts 和列表时,不能保证这一点,至少对于所有当前版本的 Python 来说不是这样

        然后因为 YAML 1.2(2009 年发布的规范)是 YAML,您可以只使用保留密钥顺序的 YAML 1.2 库 在加载转储时将其转换为您想要的格式。自从 PyYAML 仍然停留在 2005 年发布的 YAML 1.1 规范,你 不能使用它,但你可以使用ruamel.yaml(免责声明我是 该软件包的作者)。

        唯一的“问题”是ruamel.yaml 也会保留 输入的(流)样式。这正是你不想要的。

        因此,您必须递归遍历数据结构并更改 包含该信息的属性:

        import sys
        import ruamel.yaml
        
        def block_style(d):
            if isinstance(d, dict):
                d.fa.set_block_style()
                for key, value in d. items():
                    try:
                        if '{' in value:
                            d[key] = ruamel.yaml.scalarstring.DoubleQuotedScalarString(value)
                    except TypeError:
                        pass
                    block_style(value)
            elif isinstance(d, list):
                d.fa.set_block_style()
                for elem in d:
                    block_style(elem)
        
        yaml = ruamel.yaml.YAML()
        
        with open('input.json') as fp:
            data = yaml.load(fp)
        
        block_style(data)
        
        yaml.dump(data, sys.stdout)
        

        给出:

        - job-template:
            name: "{name}_job"
            description: job description
            project-type: multibranch
            number-to-keep: 30
            days-to-keep: 30
            scm:
            - git:
                url: "{git_url}"
        

        上述方法同样适用于 Python2 和 Python3

        '{' 的额外代码测试是在不能表示为纯标量的字符串周围强制使用双引号。默认情况下,如果不需要 YAML 双引号标量中可用的额外转义序列来表示字符串,ruamel.yaml 将使用单引号标量。

        【讨论】:

          猜你喜欢
          • 2016-02-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-23
          相关资源
          最近更新 更多