【问题标题】:YAML mapping order not preserved when using alias and yamlordereddictloader loader使用别名和 yamlordereddictloader 加载程序时未保留 YAML 映射顺序
【发布时间】:2020-10-22 09:59:54
【问题描述】:

我想将 YAML 文件作为 OrderedDict 加载到 Python 中。我正在使用yamlordereddictloader 来保留订单。

但是,我注意到别名对象“过早”放置在输出的 OrderedDict 中。

如何在读入 Python 时保留此映射的顺序,最好是 OrderedDict?不编写一些自定义解析是否可以达到这个结果?

注意事项:

  • 我并不特别关心使用的方法,只要最终结果相同即可。
  • 使用序列而不是映射是有问题的,因为它们会导致嵌套输出,而且我不能简单地展平所有内容(一些嵌套是合适的)。
  • 当我尝试仅使用 !!omap 时,我似乎无法将别名映射 (d1.dt) 合并到 d2 映射中。
  • 我在 Python 3.6 中,如果我不使用此加载程序或 !!omap 订单不保留(显然与此处的顶部“更新”相反:https://stackoverflow.com/a/21912744/2343633
import yaml
import yamlordereddictloader

yaml_file = """
d1:
  id:
    nm1: val1
  dt: &dt
    nm2: val2
    nm3: val3

d2: # expect nm4, nm2, nm3
  nm4: val4
  <<: *dt
"""

out = yaml.load(yaml_file, Loader=yamlordereddictloader.Loader)
keys = [x for x in out['d2']]
print(keys) # ['nm2', 'nm3', 'nm4']
assert keys==['nm4', 'nm2', 'nm3'], "order from YAML file is not preserved, aliased keys placed too early"

【问题讨论】:

    标签: yaml python-3.6 pyyaml ordereddictionary


    【解决方案1】:

    不写一些自定义解析也能达到这个结果吗?

    是的。您需要从SafeConstructor 覆盖方法flatten_mapping。这是一个基本的工作示例:

    import yaml
    import yamlordereddictloader
    from yaml.constructor import *
    from yaml.reader import *
    from yaml.parser import *
    from yaml.resolver import *
    from yaml.composer import *
    from yaml.scanner import *
    from yaml.nodes import *
    
    class MyLoader(yamlordereddictloader.Loader):
      def __init__(self, stream):
        yamlordereddictloader.Loader.__init__(self, stream)
        
      # taken from here and reengineered to keep order:
      # https://github.com/yaml/pyyaml/blob/5.3.1/lib/yaml/constructor.py#L207
      def flatten_mapping(self, node):
        merged = []
        def merge_from(node):
          if not isinstance(node, MappingNode):
            raise yaml.ConstructorError("while constructing a mapping",
                node.start_mark, "expected mapping for merging, but found %s" %
                node.id, node.start_mark)
          self.flatten_mapping(node)
          merged.extend(node.value)
        for index in range(len(node.value)):
          key_node, value_node = node.value[index]
          if key_node.tag == u'tag:yaml.org,2002:merge':
            if isinstance(value_node, SequenceNode):
               for subnode in value_node.value:
                 merge_from(subnode)
            else:
              merge_from(value_node)
          else:
           if key_node.tag == u'tag:yaml.org,2002:value':
             key_node.tag = u'tag:yaml.org,2002:str'
           merged.append((key_node, value_node))
        node.value = merged
    
    yaml_file = """
    d1:
      id:
        nm1: val1
      dt: &dt
        nm2: val2
        nm3: val3
    
    d2: # expect nm4, nm2, nm3
      nm4: val4
      <<: *dt
    """
    
    out = yaml.load(yaml_file, Loader=MyLoader)
    keys = [x for x in out['d2']]
    print(keys)
    assert keys==['nm4', 'nm2', 'nm3'], "order from YAML file is not preserved, aliased keys placed too early"
    

    这并不是最好的性能,因为它基本上在加载期间从所有映射中复制所有键值对,但它正在工作。性能增强留给读者作为练习:)。

    【讨论】:

      猜你喜欢
      • 2016-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-03
      • 2023-04-05
      • 2012-06-23
      相关资源
      最近更新 更多