在Python中,如何使用不使用YAML标记的数据绑定解析YAML?

时间:2016-01-25 04:43:18

标签: python yaml pyyaml

我正在创建一个YAML文件来保存与XML项目相关的元数据,并且我只使用Python作为测试工具 - 它不是一个python项目。

所以,我想编写一些类来保存数据,我很惊讶地发现让PyYAML自动将文件读入这些类的标准方法是使用YAML标记(例如{{1}实例化Monster对象)。

我不想使用标签,因为我希望这些YAML文件尽可能便携 - 任何想要的人都可以使用他们选择的语言(像JSON一样可移植)。

在我看来,数据绑定信息不属于YAML文件,作为一般原则,但谷歌搜索一段时间后,我似乎无法找到其他人抱怨这一点。我确实发现很多人都在问如何使用无法识别的标签来解析YAML,如何忽略标签等等 - 所以我认为标签确实会给数据消费者造成问题。

我喜欢使用内省和注释Jackson的工作方式(用于在Java中解析JSON)。在Python中解析YAML有类似的东西吗?

以下是我正在讨论的YAML内容的一个示例:

!Monster

我想将其映射到Split类的对象列表,其中包含一系列属性,包括一个乐器列表和一个歌曲列表。

1 个答案:

答案 0 :(得分:1)

手动方式是定义以下内容:

class Instrument(object):
    def __getstate__(self):
        return {
            "path": self.path,
            "url": self.url,
        }

    def __setstate__(self, state):
        self.path = state["path"]
        self.url = state["url"]


class Song(object):
    def __getstate__(self):
        return {
            "name": self.name,
            "genre": self.genre,
        }

    def __setstate__(self, state):
        self.name = state["name"]
        # Note that this can be non-present -> store None
        self.genre = state.get("genre")
        self.year = state.get("year")


class Split(object):
    def __getstate__(self):
        return {
            "name": self.name,
            "color": self.color,
            "instruments": [i.__getstate__() for i in self.instruments],
            "songs": [s.__getstate__() for s in self.songs],
        }

    def __setstate__(self, state):
        self.name = state["name"]
        self.color = state["color"]
        self.instruments = list()
        for i_state in state["instruments"]:
            i = Instrument.__new__(Instrument)
            i.__setstate__(i_state)
            self.instruments.append(i)

        self.songs = list()
        for s_state in state["songs"]:
            s = Song.__new__(Song)
            s.__setstate__(s_state)
            self.songs.append(s)

请注意,我使用__getstate____setstate__只是为了与pickle以及一些典型应用程序保持友好关系,但您可以使用其他名称。

然后,假设您在名为data的变量中包含YAML文档,您只需:

structured_data = yaml.load(data)
list_of_splits = list()
for s_state in structured_data:
    s = Split.__new__(Split)
    s.__setstate__(s_state)
    list_of_splits.append(s)

structured_data将包含变体内容的词典列表。但这一切都将是简单的类型。 __setstate__递归过程可确保所有内容都符合预期 - 但您必须定义预期的内容,因为我们没有类型或任何其他类型的提示。

可以定义某种提示以自动化此行为。我想的是:

class Split(AutoMagicYAMLObject):
    name = StringField()
    color = StringField()
    instruments = ListField(Instrument)
    songs = ListField(Song)

...但这将是一个有趣的应用程序,充满了极端案例和精巧的设计决策:)我不知道任何现有的包做类似的事情,所以无法帮助更多。