Модуль Python "yaml", преобразующий неожиданный YAML из формата JSON - PullRequest
0 голосов
/ 18 января 2019

Я пытаюсь преобразовать данные JSON в формат YAML, но получаю неожиданный вывод YAML

Используются онлайн-инструменты для преобразования JSON в YAML, который дает ожидаемый результат YAML. Но когда тот же JSON используется в приведенном ниже коде Python, получается неожиданный другой результат.

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}'}

Ответы [ 4 ]

0 голосов
/ 20 января 2019

Изменение порядка в PyYAML препятствует редактированию в оба конца в YAML-файлах, и ряд других анализаторов пытались это исправить.

Стоит обратить внимание на Ruamel.yaml , который написан на обзорной странице :

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

Это более подробно обсуждается здесь . Он описывается как замена для PyYAML, поэтому с ним будет легко экспериментировать в вашей среде.

0 голосов
/ 18 января 2019

Проблема в коде 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}'}}]}}]

Если вопрос касался стиля представления для последнего уровня dict {"url": "{git_url}"}, ответ был дан @ Rakesh

0 голосов
/ 18 января 2019

Прежде всего вы должны просто оставить свой шаблон работы в файле 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 & lists в ваш код, по крайней мере, не для всех текущих версий 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

Дополнительное тестирование кода для '{' заключается в применении двойных кавычек вокруг строк, которые нельзя представить в виде простых скаляров. По умолчанию ruamel.yaml будет использовать скаляры с одинарными кавычками, если дополнительные экранирующие последовательности, доступные в скалярах YAML с двойными кавычками, не нужны для представления строки.

0 голосов
/ 18 января 2019

Использование default_flow_style=False

Ex:

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)  
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...