在 ruamel.yaml 往返解析中保留空消息



使用 ruamel.yaml,YAML 的往返解析输出

a: {b: }

a: {b: !!null ''}

有没有办法保留空消息,或覆盖None表示器以输出空消息,如上所示?

这不是微不足道的,我不确定下面提供的解决方案是否没有不良副作用

非平凡的原因与多件事有关:

  • 您正在使用不太易读的流样式

    a: {b: } 
    

    而不是块样式:

    a:
    b:
    

    后者往返没有变化

  • b的空值加载为None,我无法在 ruamel.yaml 中对其进行子类化,因此样式信息无法附加到该值,您必须依赖"默认"发射器(在您的情况下,它不会做您想要的)。
  • a: {b:} 
    

    与您的完全不同

    a: {b: } 
    

    目前,发射器会确保安全并将标签信息插入流中。

使用该背景信息,您可以将None的表示样式强制为空字符串,并根据该黑客对发射器

进行破解:
import sys
import ruamel.yaml
yaml_str = """
a: {b: }
"""
class MyEmitter(ruamel.yaml.emitter.Emitter):
def choose_scalar_style(self):
# override selection for 'null'
if self.analysis is None:
self.analysis = self.analyze_scalar(self.event.value)
if self.event.style == '"' or self.canonical:
return '"'
if (not self.event.style or self.event.style == '?') and 
(self.event.implicit[0] or not self.event.implicit[2]):
if (not (self.simple_key_context and
(self.analysis.empty or self.analysis.multiline)) and
(self.flow_level and self.analysis.allow_flow_plain or
(not self.flow_level and self.analysis.allow_block_plain))):
return ''
if (self.event.style == '') and self.event.tag == 'tag:yaml.org,2002:null' and 
(self.event.implicit[0] or not self.event.implicit[2]):
if self.flow_level and not self.analysis.allow_flow_plain:
return ''
self.analysis.allow_block = True
if self.event.style and self.event.style in '|>':
if (not self.flow_level and not self.simple_key_context and
self.analysis.allow_block):
return self.event.style
if not self.event.style and self.analysis.allow_double_quoted:
if "'" in self.event.value or 'n' in self.event.value:
return '"'
if not self.event.style or self.event.style == ''':
if (self.analysis.allow_single_quoted and
not (self.simple_key_context and self.analysis.multiline)):
return '''
return '"'
def process_scalar(self):
# if style '' and tag is 'null' insert empty space
if self.analysis is None:
self.analysis = self.analyze_scalar(self.event.value)
if self.style is None:
self.style = self.choose_scalar_style()
split = (not self.simple_key_context)
if self.sequence_context and not self.flow_level:
self.write_indent()
if self.style == '"':
self.write_double_quoted(self.analysis.scalar, split)
elif self.style == ''':
self.write_single_quoted(self.analysis.scalar, split)
elif self.style == '>':
self.write_folded(self.analysis.scalar)
elif self.style == '|':
self.write_literal(self.analysis.scalar)
elif self.event.tag == 'tag:yaml.org,2002:null':
self.stream.write(u' ')  # not sure if this doesn't break other things
else:
self.write_plain(self.analysis.scalar, split)
self.analysis = None
self.style = None
if self.event.comment:
self.write_post_comment(self.event)
class MyRepresenter(ruamel.yaml.representer.RoundTripRepresenter):
def represent_none(self, data):
if len(self.represented_objects) == 0 and not self.serializer.use_explicit_start:
# this will be open ended (although it is not yet)
return self.represent_scalar(u'tag:yaml.org,2002:null', u'null')
return self.represent_scalar(u'tag:yaml.org,2002:null', u'', style='')
MyRepresenter.add_representer(type(None),
MyRepresenter.represent_none)

yaml = ruamel.yaml.YAML()
yaml.Emitter = MyEmitter
yaml.Representer = MyRepresenter
data = yaml.load(yaml_str)
yaml.dump(data, sys.stdout)

这给了:

a: {b: }

最新更新