python - parse - ¿Cómo puedo controlar qué forma escalar utiliza PyYAML para mis datos?
python pyyaml (4)
¿ Está basado en las bibliotecas yaml de Python que admiten el volcado de cadenas largas como bloques literales o bloques plegados?
import yaml
from collections import OrderedDict
class quoted(str):
pass
def quoted_presenter(dumper, data):
return dumper.represent_scalar(''tag:yaml.org,2002:str'', data, style=''"'')
yaml.add_representer(quoted, quoted_presenter)
class literal(str):
pass
def literal_presenter(dumper, data):
return dumper.represent_scalar(''tag:yaml.org,2002:str'', data, style=''|'')
yaml.add_representer(literal, literal_presenter)
def ordered_dict_presenter(dumper, data):
return dumper.represent_dict(data.items())
yaml.add_representer(OrderedDict, ordered_dict_presenter)
d = OrderedDict(short=quoted("Hello"), long=literal("Line1/nLine2/nLine3/n"))
print(yaml.dump(d))
Salida
short: "Hello"
long: |
Line1
Line2
Line3
Tengo un objeto con un atributo de cadena corta y un atributo de cadena multilínea larga. Quiero escribir la cadena corta como un escalar YAML entre comillas, y la cadena multilínea como un escalar literal:
my_obj.short = "Hello"
my_obj.long = "Line1/nLine2/nLine3"
Me gustaría que el YAML se vea así:
short: "Hello"
long: |
Line1
Line2
Line3
¿Cómo puedo ordenar a PyYAML que haga esto? Si llamo a yaml.dump(my_obj)
, produce una salida similar a un dict:
{long: ''line1
line2
line3
'', short: Hello}
(No estoy seguro de por qué el tiempo es doble espacio así ...)
¿Puedo dictar a PyYAML cómo tratar mis atributos? Me gustaría afectar tanto el orden como el estilo.
Al enamorarme del enfoque de @lbt , obtuve este código:
import yaml
def str_presenter(dumper, data):
if len(data.splitlines()) > 1: # check for multiline string
return dumper.represent_scalar(''tag:yaml.org,2002:str'', data, style=''|'')
return dumper.represent_scalar(''tag:yaml.org,2002:str'', data)
yaml.add_representer(str, str_presenter)
Hace que cada cadena multilínea sea un bloque literal.
Estaba tratando de evitar la parte de parches de mono. Crédito completo para @lbt y @JFSebastian.
Puede usar ruamel.yaml
y su RoundTripLoader / Dumper (descargo de responsabilidad: soy el autor de ese paquete) además de hacer lo que quiere, es compatible con la especificación YAML 1.2 (desde 2009) y tiene otras mejoras:
import sys
from ruamel.yaml import YAML
yaml_str = """/
short: "Hello" # does keep the quotes, but need to tell the loader
long: |
Line1
Line2
Line3
folded: >
some like
explicit folding
of scalars
for readability
"""
yaml = YAML()
yaml.preserve_quotes = True
data = yaml.load(yaml_str)
yaml.dump(data, sys.stdout)
da:
short: "Hello" # does keep the quotes, but need to tell the loader
long: |
Line1
Line2
Line3
folded: >
some like
explicit folding
of scalars
for readability
(incluyendo el comentario, comenzando en la misma columna que antes)
También puede crear esta salida desde cero, pero luego debe proporcionar la información adicional, por ejemplo, las posiciones explícitas sobre dónde plegarse.
Quería que cualquier entrada con un /n
en ella fuera un literal de bloque. Usando el código en yaml/representer.py
como base obtuve:
# -*- coding: utf-8 -*-
import yaml
def should_use_block(value):
for c in u"/u000a/u000d/u001c/u001d/u001e/u0085/u2028/u2029":
if c in value:
return True
return False
def my_represent_scalar(self, tag, value, style=None):
if style is None:
if should_use_block(value):
style=''|''
else:
style = self.default_style
node = yaml.representer.ScalarNode(tag, value, style=style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
return node
a={''short'': "Hello", ''multiline'': """Line1
Line2
Line3
""", ''multiline-unicode'': u"""Lêne1
Lêne2
Lêne3
"""}
print(yaml.dump(a))
print(yaml.dump(a, allow_unicode=True))
yaml.representer.BaseRepresenter.represent_scalar = my_represent_scalar
print(yaml.dump(a))
print(yaml.dump(a, allow_unicode=True))
Salida
{multiline: ''Line1
Line2
Line3
'', multiline-unicode: "L/xEAne1/nL/xEAne2/nL/xEAne3/n", short: Hello}
{multiline: ''Line1
Line2
Line3
'', multiline-unicode: ''Lêne1
Lêne2
Lêne3
'', short: Hello}
After override
multiline: |
Line1
Line2
Line3
multiline-unicode: "L/xEAne1/nL/xEAne2/nL/xEAne3/n"
short: Hello
multiline: |
Line1
Line2
Line3
multiline-unicode: |
Lêne1
Lêne2
Lêne3
short: Hello