Ich versuche, JSON-Daten in das YAML-Format zu konvertieren, erhalte jedoch eine unerwartete YAML-Ausgabe

Verwendete Online-Tools zum Konvertieren von JSON in YAML, wodurch die erwartete YAML-Ausgabe erzielt wird. Wenn jedoch derselbe JSON im folgenden Python-Code verwendet wird, wird ein unerwartet anderes Ergebnis erzielt.

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"))   

Erwarten unter YAML-Daten:

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

Unter das YAML-Format gelangen:

 - job-template:
     days-to-keep: 30
     description: job description
     name: '{name}_job'
     number-to-keep: 30
     project-type: multibranch
     scm:
     - git: {url: '{git_url}'}
0
vsmahajan 18 Jän. 2019 im 15:04

4 Antworten

Beste Antwort

Verwenden Sie default_flow_style=False

Beispiel:

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)  
1
Rakesh 18 Jän. 2019 im 12:08

Die Änderung der Reihenfolge in PyYAML ist ein Hindernis für Round-Trip-Änderungen an YAML-Dateien, und eine Reihe anderer Parser haben versucht, dies zu beheben.

Ein Blick wert ist Ruamel.yaml, auf dem Übersichtsseite:

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

Ein vom Autor bereitgestelltes Codebeispiel zeigt dies:

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

Dies wird hier ausführlicher besprochen. Es wird als Ersatz für PyYAML beschrieben und sollte daher in Ihrer Umgebung leicht zu experimentieren sein.

0
Mazerunner72 20 Jän. 2019 im 11:40

Zunächst sollten Sie Ihre Jobvorlage einfach in einer JSON-Datei belassen, z. B. 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}"                                                  
          }                                                                     
        }                                                                       
      ]                                                                         
    }                                                                           
  }                                                                             
]

Auf diese Weise können Sie Ihr Skript einfacher anpassen, um verschiedene Dateien zu verarbeiten. Dies garantiert auch, dass die Schlüssel in Ihren JSON-Objekten geordnet sind. Dies ist nicht garantiert, wenn Sie den JSON als Dicts & Listen in Ihren Code aufnehmen, zumindest nicht für alle aktuellen Versionen von Python

Dann, weil YAML 1.2 (Spezifikation im Jahr 2009 veröffentlicht) eine Obermenge von ist YAML, Sie können einfach eine YAML 1.2-Bibliothek verwenden, die die Schlüsselreihenfolge beibehält beim Laden-Dumping, um dies in das gewünschte Format zu konvertieren. Schon seit PyYAML bleibt bei der 2005 herausgegebenen YAML 1.1-Spezifikation, Sie kann das nicht verwenden, aber Sie können ruamel.yaml verwenden (Haftungsausschluss Ich bin der Autor dieses Pakets).

Das einzige "Problem" ist, dass ruamel.yaml auch das beibehalten wird (Flow-) Stil für Ihre Eingabe. Genau das wollen Sie nicht.

Sie müssen also rekursiv über die Datenstruktur gehen und das Attribut ändern, das diese Informationen enthält:

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)

Was gibt:

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

Das Obige funktioniert für Python2 und Python3 gleich gut

Der zusätzliche Codetest für '{' besteht darin, doppelte Anführungszeichen um die Zeichenfolgen zu erzwingen, die nicht als einfache Skalare dargestellt werden können. Standardmäßig würde ruamel.yaml Skalare in einfachen Anführungszeichen verwenden, wenn die zusätzlichen Escape-Sequenzen, die in YAML-Skalaren mit doppelten Anführungszeichen verfügbar sind, nicht zur Darstellung der Zeichenfolge benötigt werden.

-1
Anthon 18 Jän. 2019 im 14:29

Das Problem liegt im Python-Code: a dict ist ein ungeordneter Container. pprint gibt nur die gleiche Reihenfolge Ihrer Yaml-Ausgabe an:

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

Wenn die Frage den Stil der Darstellung für das Diktat der letzten Ebene {"url": "{git_url}"} betraf, wurde die Antwort von @Rakesh

1
Serge Ballesta 18 Jän. 2019 im 13:36