【问题标题】:How to parse nested dictionary inside a list in yaml?如何解析yaml列表中的嵌套字典?
【发布时间】:2019-04-14 15:40:06
【问题描述】:

我正在解析 YAML 文件以搜索任意键的值。目前我可以在第一级解析任何字典,但不能解析嵌套字典。

我尝试修改https://stackoverflow.com/a/55608627 的示例以解析列表中的字典,但这会导致错误:

AttributeError: 'CommentedSeq' object has no attribute 'items'

查看http://yaml-online-parser.appspot.com/ 的规范输出时,它显示有一个映射和一个序列,我无法解释。

未修改的解析函数不会输出任何错误,但是它看不到列表中的任何内容。

修改后的解析函数返回上面的AttributeError。

示例 YAML 文件:https://pastebin.com/BhwyPa7V

完整项目:https://github.com/Just-Insane/helm-vault/blob/master/vault.py

解析函数(未修改):

def dict_walker(node, pattern, path=None):
    path = path if path is not None else ""
    for key, value in node.items():
        if isinstance(value, dict):
            dict_walker(value, pattern=pattern, path=f"{path}/{key}")
        elif value == pattern:
            if action == "enc":
                node[key] = input(f"Input a value for {path}/{key}: ")
                vault_write(node[key], path, key)
            elif (action == "dec") or (action == "view") or (action == "edit"):
                value = vault_read(path, key)
                node[key] = value

解析函数(修改):

def dict_walker(node, pattern, path=None):
    path = path if path is not None else ""
    for key, value in node.items():
        if isinstance(value, dict):
            dict_walker(value, pattern=pattern, path=f"{path}/{key}")
        elif isinstance(value, list):
            for item in value:
                for value in dict_walker(value, pattern=pattern, path=f"{path}/{key}"):
                    if value == pattern:
                        if action == "enc":
                            node[key] = input(f"Input a value for {path}/{key}: ")
                            vault_write(node[key], path, key)
                        elif (action == "dec") or (action == "view") or (action == "edit"):
                            value = vault_read(path, key)
                            node[key] = value
        elif value == pattern:
            if action == "enc":
                node[key] = input(f"Input a value for {path}/{key}: ")
                vault_write(node[key], path, key)
            elif (action == "dec") or (action == "view") or (action == "edit"):
                value = vault_read(path, key)
                node[key] = value

预期结果:

嵌套字典被解析,里面的值可以修改成功。

实际结果:

  1. 使用未修改的代码,列表内的值根本看不到。

  2. 使用修改后的代码,出现CommentedSeq导致的属性错误。目前尚不清楚为什么它没有被解析为列表。

【问题讨论】:

    标签: python-3.x ruamel.yaml


    【解决方案1】:

    正如我在您链接到的答案中指出的那样,解析已完全完成 甚至在 yaml.load() 方法返回之前。你所做的是 遍历加载的数据。

    您的 dict_walker() 基于 find() 函数,它仅适用于 来自the question 的无趣 YAML 输入 我回答了。它假设 YAML:

    仅由(纯)标量(字符串)、映射和映射键(标量)组成。

    那里提供的lookup 函数可以处理序列,因此 您需要将 dict_walker() 函数建立在什么基础之上(以及 功能,你有它有一个合适的名字:它只能走过去 在节点上调用.items() 方法的字典假定nodedict)。

    假设您的示例 YAML 在文件 input.yaml 中,以下内容确实是 完整的树并到达嵌套在列表中的字典(从嵌套的映射创建 在您的 YAML 中的序列内):

    import sys
    from pathlib import Path
    import ruamel.yaml
    
    in_file = Path('input.yaml')
    
    action = "view"
    
    def vault_read(path, key):
        # dummy function to show functionality
        vault = {
            ("/spec/acme", "email"): "repl_0",
            ("/spec/acme/dns01/providers/0/cloudflare", "email"): "repl_1",
            ("/spec/acme/dns01/providers/0/cloudflare/apiKeySecretRef", "key"): 42,
        }
        return vault.get((path, key), "not found")
    
    def walk_data(node, pattern, path=None):
       if path is None:
           path = ""
       if isinstance(node, dict):
           for key, value in node.items():
               if value == pattern:
                   if action == "enc":
                       node[key] = input(f"Input a value for {path}/{key}: ")
                       vault_write(node[key], path, key)
                   elif (action == "dec") or (action == "view") or (action == "edit"):
                       node[key] = vault_read(path, key)
               else:
                   walk_data(value, pattern, path=f"{path}/{key}")
       elif isinstance(node, list):
           for idx, item in enumerate(node):
               walk_data(item, pattern, path=f"{path}/{idx}")
    
    
    yaml = ruamel.yaml.YAML()
    data = yaml.load(in_file)
    
    walk_data(data, "changeme")
    
    yaml.dump(data, sys.stdout)
    

    给出:

    apiVersion: certmanager.k8s.io/v1alpha1
    kind: ClusterIssuer
    metadata:
      name: letsencrypt-production
    spec:
      acme:
        # You must replace this email address with your own.
        # Let's Encrypt will use this to contact you about expiring
        # certificates, and issues related to your account.
        email: repl_0
        server: https://acme-v02.api.letsencrypt.org/directory
        privateKeySecretRef:
          # Secret resource used to store the account's private key.
          name: letsencrypt-production
        # Enable the HTTP01 challenge mechanism for this Issuer
        dns01:
          providers:
          - name: prod-cloudflare
            cloudflare:
              email: repl_1
              apiKeySecretRef:
                name: cloudflare-api-key-secret
                key: 42
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-25
      • 2018-08-23
      • 1970-01-01
      • 2018-12-21
      • 1970-01-01
      • 2018-09-05
      相关资源
      最近更新 更多