如何在python中解析深度嵌套的yaml数据结构

时间:2019-04-09 21:58:53

标签: python yaml

我们有一个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

2 个答案:

答案 0 :(得分:0)

也许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)

答案 1 :(得分:0)

您的标题使您看起来对什么有点困惑 或至少与术语有关:尽管“ 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不正确。

尽管使用的标量类型不影响递归查找, 您可能想要一个更通用的解决方案,可以使用 (嵌套)序列,标记的节点以及复杂的映射键。

假设您的输入文件稍微复杂一点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。

上面提到的代码片段(@aaaaa正在使用)正在中断 由于(直接)嵌套了序列/列表,因此加载了YAML