Как я могу контролировать, какую скалярную форму PyYAML использует для моих данных?
У меня есть объект с коротким строковым атрибутом и длинный многострочный строковый атрибут. Я хочу написать короткую строку в виде скаляризованного YAML-скаляра и многострочной строки как литерал-скаляр:
my_obj.short = "Hello"
my_obj.long = "Line1\nLine2\nLine3"
Я бы хотел, чтобы YAML выглядел так:
short: "Hello"
long: |
Line1
Line2
Line3
Как я могу инструктировать PyYAML для этого? Если я вызываю yaml.dump(my_obj)
, он выдает диктофонный вывод:
{long: 'line1
line2
line3
', short: Hello}
(Не уверен, почему длинный двойной интервал...)
Могу ли я диктовать PyYAML, как обращаться с моими атрибутами? Я хотел бы повлиять на порядок и стиль.
Ответы
Ответ 1
На основе Любые библиотеки yaml в Python, которые поддерживают сброс длинных строк в виде блоков или сложенных блоков?
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))
Выход
short: "Hello"
long: |
Line1
Line2
Line3
Ответ 2
Мне нужен любой ввод с \n
в нем, чтобы быть блочным литералом. Используя код в yaml/representer.py
в качестве базы, я получил:
# -*- 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))
Выход
{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
Ответ 3
Влюбившись в @lbt approach, я получил этот код:
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)
Он делает каждую многострочную строку блочным литералом.
Я пытался избежать частичного исправления обезьян.
Полный кредит для @lbt и @J.F.Sebastian.
Ответ 4
Вы можете использовать ruamel.yaml
и его RoundTripLoader/Dumper (отказ от ответственности: я автор этого пакета), кроме того, что вы хотите, он поддерживает спецификацию YAML 1.2 (с 2009 года) и имеет несколько других улучшений:
import sys
import ruamel.yaml as yaml
yaml_str = """\
short: "Hello" # does keep the quotes, but need to tell the loader
long: |
Line1
Line2
Line3
"""
data = yaml.round_trip_load(yaml_str, preserve_quotes=True)
yaml.round_trip_dump(data, sys.stdout)
дает:
short: "Hello" # does keep the quotes, but need to tell the loader
long: |
Line1
Line2
Line3
(включая комментарий, начиная с того же столбца, что и раньше)