【问题标题】:How to parse deeply nested yaml data structures in python如何在python中解析深度嵌套的yaml数据结构
【发布时间】:2019-04-09 21:58:52
【问题描述】:

我们有一个类似于以下内容的 YAML 文件:

all:
  children:
    allnetxsites:
      children:
        netxsites:
          hosts:
            bar.:
              ansible_ssh_host: bart.j
              domain: bart.local.domain
              nfs: lars.local.domain

我将如何获取值 bar. 和键值 nfs

Python 代码:

import yaml
with open("/Users/brendan_vandercar/sites.yaml", 'r') as stream:
    data_loaded = yaml.load(stream)

for element in data_loaded:
    name = "element"['all']['children']['allnetxsites']['children']['netxsites']['hosts']['bart']['nfs'][0]
    print(name)

我想要得到的是这个脚本的列表输出,它具有以下内容:

Domain: bart.local.domain
NFS: lars.local.domain

【问题讨论】:

    标签: python yaml


    【解决方案1】:

    你的标题让你看起来对什么是有点困惑 继续,或者至少关于术语:虽然“YAML 数据 结构”可能被解释为“Python 数据结构”的简写 从 YAML 文档加载”,您无需进一步解析该数据 结构体。任何解析都是作为 YAML 加载的一部分完成的 文档和解析甚至在yaml.load() 之前就已经完全完成 返回。作为加载的结果,您在 Python 中有一个数据结构,并且 你“只是”需要在嵌套的 Python 数据结构中查找一个键 递归遍历该数据结构。


    你的 YAML 例子有点无趣,因为它只代表一个 真实 YAML 的一小部分,因为您的 YAML 仅包含(普通)标量 它们是标量的字符串、映射和映射键。

    为了遍历该数据结构,提供了递归函数@aaaaaa 的简化版本 会做的:

    import sys
    import yaml
    
    yaml_str = """\
    all:
      children:
        allnetxsites:
          children:
            netxsites:
              hosts:
                bar.:
                  ansible_ssh_host: bart.j
                  domain: bart.local.domain
                  nfs: lars.local.domain
    """
    
    data = yaml.safe_load(yaml_str)
    
    def find(key, dictionary):
        # everything is a dict
        for k, v in dictionary.items():
            if k == key:
                yield v
            elif isinstance(v, dict):
                for result in find(key, v):
                    yield result
    
    for x in find("nfs", data):
        print(x)
    

    打印预期的:

    lars.local.domain
    

    我已经简化了函数find,因为在版本中的列表处理 snippet 不正确。

    虽然使用的标量种类不影响递归查找, 您可能想要一个更通用的解决方案来处理 YAML (嵌套)序列、标记节点和复杂的映射键。

    假设您的输入文件稍微复杂一点input.yaml

    all:
      {a: x}: !xyz
      - [k, l, 0943]
      children:
        allnetxsites:
          children:
            netxsites:
              hosts:
                bar.:
                  ansible_ssh_host: bart.j
                  domain: bart.local.domain
                  nfs: lars.local.domain
    

    你可以使用ruamel.yaml(免责声明:我是那个包的作者)来做:

    import sys
    from pathlib import Path
    import ruamel.yaml
    
    in_file = Path('input.yaml')
    
    yaml = ruamel.yaml.YAML()
    data = yaml.load(in_file)
    
    def lookup(sk, d, path=[]):
       # lookup the values for key(s) sk return as list the tuple (path to the value, value)
       if isinstance(d, dict):
           for k, v in d.items():
               if k == sk:
                   yield (path + [k], v)
               for res in lookup(sk, v, path + [k]):
                   yield res
       elif isinstance(d, list):
           for item in d:
               for res in lookup(sk, item, path + [item]):
                   yield res
    
    for path, value in lookup("nfs", data):
        print(path, '->', value)
    

    给出:

    ['all', 'children', 'allnetxsites', 'children', 'netxsites', 'hosts', 'bar.', 'nfs'] -> lars.local.domain
    

    由于 PyYAML 只解析 YAML 1.1 的一个子集并且加载更少的 也就是说,它无法处理 input.yaml 中的有效 YAML。

    上面提到的sn-p,@aaaaa 正在使用的那个,将会中断 由于(直接)嵌套的序列/列表而加载的 YAML

    【讨论】:

      【解决方案2】:

      也许this snippet 会给你一些帮助

      def find(key, dictionary):
          for k, v in dictionary.iteritems():
              if k == key:
                  yield v
              elif isinstance(v, dict):
                  for result in find(key, v):
                      yield result
              elif isinstance(v, list):
                  for d in v:
                      for result in find(key, d):
                          yield result
      

      那么你的代码就相当于

      find('nfs', data_loaded)
      

      【讨论】:

      • .iteritems() 仅在 Python 2 中可用,明年将停止使用,请改用 .items()。更成问题的是列表的第二类处理,这个find 打破了直接嵌套的列表,看起来列表是事后才添加的,而且不正确。
      猜你喜欢
      • 2013-06-08
      • 1970-01-01
      • 2023-01-07
      • 2019-07-28
      • 2015-01-15
      • 2019-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多