简体   繁体   English

如何将 YAML 文件解析/读取到 Python object 中?

[英]How to parse/read a YAML file into a Python object?

How to parse/read a YAML file into a Python object?如何将 YAML 文件解析/读取到 Python object 中?

For example, this YAML:例如,这个 YAML:

Person:
  name: XYZ

To this Python class:对此 Python class:

class Person(yaml.YAMLObject):
  yaml_tag = 'Person'

  def __init__(self, name):
    self.name = name

I am using PyYAML by the way.顺便说一句,我正在使用 PyYAML。

If your YAML file looks like this:如果您的 YAML 文件如下所示:

# tree format
treeroot:
    branch1:
        name: Node 1
        branch1-1:
            name: Node 1-1
    branch2:
        name: Node 2
        branch2-1:
            name: Node 2-1

And you've installed PyYAML like this:你已经像这样安装了PyYAML

pip install PyYAML

And the Python code looks like this: Python 代码如下所示:

import yaml
with open('tree.yaml') as f:
    # use safe_load instead load
    dataMap = yaml.safe_load(f)

The variable dataMap now contains a dictionary with the tree data.变量dataMap现在包含一个包含树数据的字典。 If you print dataMap using PrettyPrint, you will get something like:如果您使用 PrettyPrint 打印dataMap ,您将得到如下结果:

{
    'treeroot': {
        'branch1': {
            'branch1-1': {
                'name': 'Node 1-1'
            },
            'name': 'Node 1'
        },
        'branch2': {
            'branch2-1': {
                'name': 'Node 2-1'
            },
            'name': 'Node 2'
        }
    }
}

So, now we have seen how to get data into our Python program.所以,现在我们已经看到了如何将数据导入到我们的 Python 程序中。 Saving data is just as easy:保存数据同样简单:

with open('newtree.yaml', "w") as f:
    yaml.dump(dataMap, f)

You have a dictionary, and now you have to convert it to a Python object:你有一个字典,现在你必须把它转换成一个 Python 对象:

class Struct:
    def __init__(self, **entries): 
        self.__dict__.update(entries)

Then you can use:然后你可以使用:

>>> args = your YAML dictionary
>>> s = Struct(**args)
>>> s
<__main__.Struct instance at 0x01D6A738>
>>> s...

and follow " Convert Python dict to object ".并遵循“ 将 Python dict 转换为对象”。

For more information you can look at pyyaml.org and this .有关更多信息,您可以查看pyyaml.org这个

From http://pyyaml.org/wiki/PyYAMLDocumentation :来自http://pyyaml.org/wiki/PyYAMLDocumentation

add_path_resolver(tag, path, kind) adds a path-based implicit tag resolver. add_path_resolver(tag, path, kind)添加一个基于路径的隐式标签解析器。 A path is a list of keys that form a path to a node in the representation graph.路径是形成表示图中节点的路径的键列表。 Paths elements can be string values, integers, or None.路径元素可以是字符串值、整数或无。 The kind of a node can be str, list, dict, or None.节点的种类可以是 str、list、dict 或 None。

#!/usr/bin/env python
import yaml

class Person(yaml.YAMLObject):
  yaml_tag = '!person'

  def __init__(self, name):
    self.name = name

yaml.add_path_resolver('!person', ['Person'], dict)

data = yaml.load("""
Person:
  name: XYZ
""")

print data
# {'Person': <__main__.Person object at 0x7f2b251ceb10>}

print data['Person'].name
# XYZ

Here is one way to test which YAML implementation the user has selected on the virtualenv (or the system) and then define load_yaml_file appropriately:这是一种测试用户在 virtualenv(或系统)上选择的 YAML 实现,然后适当定义load_yaml_file

load_yaml_file = None

if not load_yaml_file:
    try:
        import yaml
        load_yaml_file = lambda fn: yaml.load(open(fn))
    except:
        pass

if not load_yaml_file:
    import commands, json
    if commands.getstatusoutput('ruby --version')[0] == 0:
        def load_yaml_file(fn):
            ruby = "puts YAML.load_file('%s').to_json" % fn
            j = commands.getstatusoutput('ruby -ryaml -rjson -e "%s"' % ruby)
            return json.loads(j[1])

if not load_yaml_file:
    import os, sys
    print """
ERROR: %s requires ruby or python-yaml  to be installed.

apt-get install ruby

  OR

apt-get install python-yaml

  OR

Demonstrate your mastery of Python by using pip.
Please research the latest pip-based install steps for python-yaml.
Usually something like this works:
   apt-get install epel-release
   apt-get install python-pip
   apt-get install libyaml-cpp-dev
   python2.7 /usr/bin/pip install pyyaml
Notes:
Non-base library (yaml) should never be installed outside a virtualenv.
"pip install" is permanent:
  https://stackoverflow.com/questions/1550226/python-setup-py-uninstall
Beware when using pip within an aptitude or RPM script.
  Pip might not play by all the rules.
  Your installation may be permanent.
Ruby is 7X faster at loading large YAML files.
pip could ruin your life.
  https://stackoverflow.com/questions/46326059/
  https://stackoverflow.com/questions/36410756/
  https://stackoverflow.com/questions/8022240/
Never use PyYaml in numerical applications.
  https://stackoverflow.com/questions/30458977/
If you are working for a Fortune 500 company, your choices are
1. Ask for either the "ruby" package or the "python-yaml"
package. Asking for Ruby is more likely to get a fast answer.
2. Work in a VM. I highly recommend Vagrant for setting it up.

""" % sys.argv[0]
    os._exit(4)


# test
import sys
print load_yaml_file(sys.argv[1])

I wrote an implementation using named tuples that I believe is neat because of it being a bit readable.我使用命名元组编写了一个实现,我认为它很简洁,因为它有点可读性。 It handles the cases where your dictionary is nested as well.它也处理您的字典嵌套的情况。 The parser code is as follows:解析器代码如下:

from collections import namedtuple


class Dict2ObjParser:
    def __init__(self, nested_dict):
        self.nested_dict = nested_dict

    def parse(self):
        nested_dict = self.nested_dict
        if (obj_type := type(nested_dict)) is not dict:
            raise TypeError(f"Expected 'dict' but found '{obj_type}'")
        return self._transform_to_named_tuples("root", nested_dict)

    def _transform_to_named_tuples(self, tuple_name, possibly_nested_obj):
        if type(possibly_nested_obj) is dict:
            named_tuple_def = namedtuple(tuple_name, possibly_nested_obj.keys())
            transformed_value = named_tuple_def(
                *[
                    self._transform_to_named_tuples(key, value)
                    for key, value in possibly_nested_obj.items()
                ]
            )
        elif type(possibly_nested_obj) is list:
            transformed_value = [
                self._transform_to_named_tuples(f"{tuple_name}_{i}", possibly_nested_obj[i])
                for i in range(len(possibly_nested_obj))
            ]
        else:
            transformed_value = possibly_nested_obj

        return transformed_value

I tested basic cases with the following code:我使用以下代码测试了基本案例:

x = Dict2ObjParser({
    "a": {
        "b": 123,
        "c": "Hello, World!"
    },
    "d": [
        1,
        2,
        3
    ],
    "e": [
        {
            "f": "",
            "g": None
        },
        {
            "f": "Foo",
            "g": "Bar"
        },
        {
            "h": "Hi!",
            "i": None
        }
    ],
    "j": 456,
    "k": None
}).parse()

print(x)

It gives the following output: root(a=a(b=123, c='Hello, World,'), d=[1, 2, 3], e=[e_0(f='', g=None), e_1(f='Foo', g='Bar'), e_2(h='Hi,', i=None)], j=456, k=None)它给出了以下 output: root(a=a(b=123, c='Hello, World,'), d=[1, 2, 3], e=[e_0(f='', g=None), e_1(f='Foo', g='Bar'), e_2(h='Hi,', i=None)], j=456, k=None)

Which when formatted a bit looks like:格式化时看起来像:

root(
    a=a(
        b=123,
        c='Hello, World!'
    ),
    d=[1, 2, 3],
    e=[
        e_0(
            f='',
            g=None
        ),
        e_1(
            f='Foo',
            g='Bar'
        ),
        e_2(
            h='Hi!',
            i=None
        )
    ],
    j=456,
    k=None
)

And I can access the nested fields like any other object:我可以像访问任何其他 object 一样访问嵌套字段:

print(x.a.b)  # Prints: 123

In your case, the code would ultimately look as follows:在您的情况下,代码最终将如下所示:

import yaml


with open(file_path, "r") as stream:
    nested_dict = yaml.safe_load(stream)
    nested_objt = Dict2ObjParser(nested_dict).parse()

I hope this helps!我希望这有帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM