2013-01-09 9 views
13

Ho una lista di dizionari, che voglio serializzare:formattazione PyYAML dump() Uscita

list_of_dicts = [ { 'key_1': 'value_a', 'key_2': 'value_b'}, 
        { 'key_1': 'value_c', 'key_2': 'value_d'}, 
        ... 
        { 'key_1': 'value_x', 'key_2': 'value_y'} ] 

yaml.dump(list_of_dicts, file, default_flow_style = False) 

produce i seguenti:

- key_1: value_a 
    key_2: value_b 
- key_1: value_c 
    key_2: value_d 
(...) 
- key_1: value_x 
    key_2: value_y 

ma mi piacerebbe ottenere questo:

- key_1: value_a 
    key_2: value_b 
        <-| 
- key_1: value_c  | 
    key_2: value_d  | empty lines between blocks 
(...)     | 
        <-| 
- key_1: value_x 
    key_2: value_y 

PyYAML documentation parla di dump() argomenti molto brevemente e non sembra hav e qualsiasi cosa su questo particolare argomento.

La modifica manuale del file per aggiungere nuove righe migliora notevolmente la leggibilità e la struttura viene comunque caricata in seguito, ma non ho idea di come generare il metodo dump.

E, in generale, c'è un modo per avere un maggiore controllo sulla formattazione dell'output oltre semplice rientro?

risposta

11

Non c'è un modo semplice per farlo con la libreria (oggetti Node in YAML albero di sintassi dumper sono passivi e non può emettere queste informazioni), quindi ho finito con

stream = yaml.dump(list_of_dicts, default_flow_style = False) 
file.write(stream.replace('\n- ', '\n\n- ')) 
+1

Grazie! Ho dovuto usare qualcosa di simile per la formattazione degli elenchi. PyYAML non inserisce il rientro prima di '-', mentre la libreria di consumo YAML che usiamo prevedeva dei rientri. Quindi dovevamo fare 'replace ('-', '-')' – Andrei

+0

Quel nodo passivo è vero ma irrilevante, in quanto i nodi non emettono nessuna altra informazione (cioè 'ScalarNode's non emette il proprio valore)."Emettitore" ottiene il valore di un nodo (se appropriato) e lo emette, e se si aggiungono ulteriori informazioni sui nodi e si migliorano i metodi di Emettitore rilevanti per gestire queste informazioni extra (come faccio in 'ruamel.yaml'), quindi non c'è assolutamente bisogno di fare questo tipo di post-processing di tipo grossolano, basato su stringhe. – Anthon

+0

@Andrei Con ['ruamel.yaml'] (https://pypi.python.org/pypi/ruamel.yaml) puoi impostare' yaml.indent (sequence = 3, offset = 1) 'e ottenere quell'output senza post produzione. – Anthon

0

Mentre la sua un po 'klunky , Ho avuto lo stesso obiettivo di OP. ho risolto da sottoclasse yaml.Dumper

from yaml import Dumper 

class MyDumper(Dumper): 

    def write_indent(self): 
    indent = self.indent or 0 
    if not self.indention or self.column > indent \ 
     or (self.column == indent and not self.whitespace): 
     self.write_line_break() 


    ##########$####################################### 
    # On the first level of indentation, add an extra 
    # newline 

    if indent == 2: 
     self.write_line_break() 

    ################################################## 

    if self.column < indent: 
     self.whitespace = True 
     data = u' '*(indent-self.column) 
     self.column = indent 
     if self.encoding: 
     data = data.encode(self.encoding) 
     self.stream.write(data) 

Si chiama così:

print dump(python_dict, default_flow_style=False, width=79, Dumper=MyDumper) 
1

documentazione PyYAML parla solo di argomenti dump() brevemente, perché non c'è molto da dire. Questo tipo di controllo non è fornito da PyYAML.

Per consentire la conservazione di tali righe vuote (e commento) in YAML che viene caricato, ho iniziato lo sviluppo della biblioteca ruamel.yaml, un superset del PyYAML in fase di stallo, con YAML 1.2 compatibilità, molte caratteristiche aggiunte e bug corretti. Con ruamel.yaml si può fare:

import sys 
import ruamel.yaml 

yaml_str = """\ 
- key_1: value_a 
    key_2: value_b 

- key_1: value_c 
    key_2: value_d 

- key_1: value_x # a few before this were ellipsed 
    key_2: value_y 
""" 

yaml = ruamel.yaml.YAML() 
data = yaml.load(yaml_str) 
yaml.dump(data, sys.stdout) 

e ottenere l'output esattamente la stessa stringa di input (tra cui il commento).

Si può anche costruire l'output desiderato da zero:

import sys 
import ruamel.yaml 

yaml = ruamel.yaml.YAML() 
list_of_dicts = yaml.seq([ { 'key_1': 'value_a', 'key_2': 'value_b'}, 
          { 'key_1': 'value_c', 'key_2': 'value_d'}, 
          { 'key_1': 'value_x', 'key_2': 'value_y'} ]) 

for idx in range(1, len(list_of_dicts)): 
    list_of_dicts.yaml_set_comment_before_after_key(idx, before='\n') 

ruamel.yaml.comments.dump_comments(list_of_dicts) 
yaml.dump(list_of_dicts, sys.stdout) 

La conversione utilizzando yaml.seq() è necessario per creare un oggetto che permette il fissaggio dei vuoti-linee attraverso attributi speciali.

La libreria consente inoltre di conservare/regolare le virgolette e lo stile letterale sulle stringhe, il formato di int (esadecimale, ottale, binario) e float. Oltre a specifiche di rientro separate per mappature e sequenze (sebbene non per singoli mapping o sequenze).