2016-05-13 8 views
4

Mit PyYAML, wenn ich in einer Datei mit leeren Werten in einem dict lesen:Kann ich in yaml/pyyaml ​​leer anstelle von null ausgeben?

test_str = ''' 
attrs: 
    first: 
    second: value2 
''' 

Dies gibt None für den Schlüssel first:

>>> data = yaml.load(test_str) 
>>> data 
{'attrs': {'second': 'value2', 'first': None}} 

Aber wenn mit dem Schreiben wird der None Wert ersetzt null:

>>> print(yaml.dump(data, default_flow_style=False)) 
attrs: 
    first: null 
    second: value2 

gibt es eine Möglichkeit, die Dump outpu zu formatieren t um einen leeren Skalar anstelle von null zu drucken?

+0

Gibt es einen bestimmten Grund, dass Sie null (None) Leerzeilen in Dumped Yaml benötigen? – Alik

+2

Wir erstellen ein Datenerfassungssystem für nicht-technische Benutzer und möchten, dass die Schnittstelle sehr einfach ist. Wir entschieden uns für Leerzeichen anstelle von Null für die Eingabe, und es wäre großartig, wenn die Ausgabe die Eingabe so viel wie möglich replizieren würde. – delgadom

Antwort

3

Basierend auf @ Anthons excellent answer installiert wird, konnte ich diese Lösung Handwerk:

def represent_none(self, _): 
    return self.represent_scalar('tag:yaml.org,2002:null', '') 

yaml.add_representer(type(None), represent_none) 

Basierend auf meinem Verständnis der PyYAML code, Hinzufügen eines Repräsentanten für Ein vorhandener Typ sollte einfach den vorhandenen Repräsentanten ersetzen.

+0

Wow. Verrückte Fähigkeiten. Das funktioniert total! Danke Jace und auch @Anthon – delgadom

5

Sie erhalten null weil dump() die Representer() verwendet, die SafeRepresenter() Unterklassen und None darzustellen, wird das folgende Verfahren genannt:

def represent_none(self, data): 
    return self.represent_scalar(u'tag:yaml.org,2002:null', 
           u'null') 

Als String null hartcodiert ist, gibt es keine Möglichkeit, dump() das zu ändern.

Der richtige Weg, dies in PyYAML zu lösen, ist Ihre eigene Dumper Unterklasse, die die Emitter, Serializer und Resolver von dem Standard Dumper dass dump() Anwendungen hat zu machen, aber mit Unterklasse von Representer, der darstellt, None so, wie Sie es wollen:

import sys 
import yaml 

from yaml.representer import Representer 
from yaml.dumper import Dumper 
from yaml.emitter import Emitter 
from yaml.serializer import Serializer 
from yaml.resolver import Resolver 


yaml_str = """\ 
attrs: 
    first: 
    second: value2 
""" 

class MyRepresenter(Representer): 
    def represent_none(self, data): 
     return self.represent_scalar(u'tag:yaml.org,2002:null', 
           u'') 

class MyDumper(Emitter, Serializer, MyRepresenter, Resolver): 
    def __init__(self, stream, 
      default_style=None, default_flow_style=None, 
      canonical=None, indent=None, width=None, 
      allow_unicode=None, line_break=None, 
      encoding=None, explicit_start=None, explicit_end=None, 
      version=None, tags=None): 
     Emitter.__init__(self, stream, canonical=canonical, 
       indent=indent, width=width, 
       allow_unicode=allow_unicode, line_break=line_break) 
     Serializer.__init__(self, encoding=encoding, 
       explicit_start=explicit_start, explicit_end=explicit_end, 
       version=version, tags=tags) 
     MyRepresenter.__init__(self, default_style=default_style, 
       default_flow_style=default_flow_style) 
     Resolver.__init__(self) 

MyRepresenter.add_representer(type(None), 
           MyRepresenter.represent_none) 

data = yaml.load(yaml_str) 
yaml.dump(data, stream=sys.stdout, Dumper=MyDumper, default_flow_style=False) 

gibt Ihnen:

attrs: 
    first: 
    second: value2 

Wenn das klingt wie eine Menge Overhead nur um null loszuwerden, ist es. Es gibt einige Verknüpfungen, die Sie verwenden können, und Sie können sogar versuchen, die alternative Funktion auf die vorhandene Representer zu übertragen, aber da die tatsächliche Funktion in einer Nachschlagetabelle referenziert wird (add_representer), müssen Sie mindestens diese Referenz auch behandeln.

Die weitaus einfache Lösung ist PyYAML mit ruamel.yaml ersetzen und seine round_trip Funktionalität nutzen (Disclaimer: Ich bin der Autor dieses Pakets):

import ruamel.yaml 

yaml_str = """\ 
# trying to round-trip preserve empty scalar 
attrs: 
    first: 
    second: value2 
""" 

data = ruamel.yaml.round_trip_load(yaml_str) 
assert ruamel.yaml.round_trip_dump(data) == yaml_str 

abgesehen von None als leeren Skalar emittiert, es auch Konserven Reihenfolge in Mapping-Keys, Kommentaren und Tag-Namen, von denen keiner PyYAML tut. ruamel.yaml folgt ebenfalls der Spezifikation YAML 1.2 (ab 2009), wobei PyYAML das ältere YAML 1.1 verwendet.


Das ruamel.yaml Paket kann mit pip von PyPI oder mit modernen Debian-basierten Distributionen, auch mit apt-get python-ruamel.yaml

Verwandte Themen