pyyaml解析器,用于列出同一键的值

2024-04-20 00:33:42 发布

您现在位置:Python中文网/ 问答频道 /正文

我知道这不是严格有效的YAML,但我想知道如何修改我的pyyaml解析器,将与重复键关联的值分组到一个列表中

因此,我有以下几点:

import yaml
from io import StringIO

data = """
NAME: Best Test
TEST: True

More: Yes

NO_LEADING_ZEROS: 0713

ENTITY:
    Name: Great
    Value: 10

ENTITY:
    Name: Even better
    Value: 11

"""

with StringIO(data) as f:
    parsed = yaml.load(f.read(), Loader=yaml.BaseLoader)
print(parsed)

assert(isinstance(parsed.get("ENTITY"), list))

我得到以下信息:

{'NAME': 'Best Test', 
'TEST': 'True', 
'ENTITY': {'Name': 'Even better', 'Value': '11'}, 
'More': 'Yes', 
'NO_LEADING_ZEROS': '0713'}

假设我不能更改“yaml”,而只能更改我的解析器,那么我应该更改什么来获得下面的字典呢

{'NAME': 'Best Test', 
'TEST': 'True', 
'ENTITY': [
  {'Name': 'Great', 'Value': '10'}
  {'Name': 'Even better', 'Value': '11'}], 
'More': 'Yes', 
'NO_LEADING_ZEROS': '0713'}

我见过这个答案,看起来很相似Getting duplicate keys in YAML using Python

但该解决方案不适用于我的用例,因为它只是将每个字段都放入一个列表中,并返回以下内容:

{'NAME': ['Best Test'], 
 'TEST': [True], 
  'ENTITY': [defaultdict(<class 'list'>, 
     {'Name': ['Great'], 'Value': [10]}), 
             defaultdict(<class 'list'>, 
     {'Name': ['Even better'], 'Value': [11]})], 
'More': [True], 
'NO_LEADING_ZEROS': [459]})

1条回答
网友
1楼 · 发布于 2024-04-20 00:33:42

您链接的问题给出了一个解决方案,只需稍加修改即可实现您的目标:

import yaml

def parse_preserving_duplicates(src):
    # We deliberately define a fresh class inside the function,
    # because add_constructor is a class method and we don't want to
    # mutate pyyaml classes.
    class PreserveDuplicatesLoader(yaml.loader.Loader):
        pass

    def map_constructor(loader, node, deep=False):
        """Walk the mapping, recording any duplicate keys.

        """
        mapping = {}
        for key_node, value_node in node.value:
            key = loader.construct_object(key_node, deep=deep)
            value = loader.construct_object(value_node, deep=deep)

            if key not in mapping:
              mapping[key] = []

            mapping[key].append(value)

        for k,v in mapping.items():
           if len(v) == 1:
             mapping[k] = v[0]
        return mapping

    PreserveDuplicatesLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, map_constructor)
    return yaml.load(src, PreserveDuplicatesLoader)

我删除了defaultdict,因为虽然它对加载很有用,但您可能不希望所有内容都是defaultdict。在构造mapping之后,我添加了一段代码,如果一个键只有一个值,则会删除该值周围的列表。这将仅为实际出现多次的键提供列表

相关问题 更多 >