我有两个YAML文件,“A”和“B”,我希望将A的内容插入到B中,要么拼接到现有的数据结构中,就像数组一样,要么作为元素的子元素,就像某个散列键的值一样。

这可能吗?怎么做?如果不是,是否有指向规范引用的指针?


当前回答

据我所知,YAML中不直接支持include,你必须自己提供一种机制,然而,这通常很容易做到。

我在我的python应用程序中使用YAML作为配置语言,在这种情况下经常定义这样的约定:

>>> main.yml <<<
includes: [ wibble.yml, wobble.yml]

然后在我的(python)代码中:

import yaml
cfg = yaml.load(open("main.yml"))
for inc in cfg.get("includes", []):
   cfg.update(yaml.load(open(inc)))

唯一的缺点是include中的变量将总是覆盖main中的变量,并且没有办法通过改变“includes:”语句在main中出现的位置来改变优先级。yml文件。

稍微不同的一点是,YAML不支持include,因为它并不是像基于文件的标记那样专门设计的。如果你在AJAX请求的响应中得到一个包含,它意味着什么?

其他回答

不幸的是,YAML在其标准中没有提供这个功能。

但是如果你正在使用Ruby,有一个gem通过扩展Ruby YAML库来提供你所要求的功能: https://github.com/entwanderer/yaml_extend

您的问题没有要求使用Python解决方案,但这里有一个使用PyYAML的解决方案。

PyYAML允许您将自定义构造函数(例如!include)附加到YAML加载器。我已经包含了一个可以设置的根目录,以便这个解决方案支持相对和绝对文件引用。

基于类的解决方案

这是一个基于类的解决方案,避免了我原始响应的全局根变量。

请参阅以下要点,了解一个类似的、更健壮的Python 3解决方案,该解决方案使用元类注册自定义构造函数。

import yaml
import os

class Loader(yaml.SafeLoader):

    def __init__(self, stream):

        self._root = os.path.split(stream.name)[0]

        super(Loader, self).__init__(stream)

    def include(self, node):

        filename = os.path.join(self._root, self.construct_scalar(node))

        with open(filename, 'r') as f:
            return yaml.load(f, Loader)

Loader.add_constructor('!include', Loader.include)

一个例子:

foo.yaml

a: 1
b:
    - 1.43
    - 543.55
c: !include bar.yaml

bar.yaml

- 3.6
- [1, 2, 3]

现在可以使用以下方法加载文件:

>>> with open('foo.yaml', 'r') as f:
>>>    data = yaml.load(f, Loader)
>>> data
{'a': 1, 'b': [1.43, 543.55], 'c': [3.6, [1, 2, 3]]}

对于Python用户,可以尝试pyyaml-include。

安装

pip install pyyaml-include

使用

import yaml
from yamlinclude import YamlIncludeConstructor

YamlIncludeConstructor.add_to_loader_class(loader_class=yaml.FullLoader, base_dir='/your/conf/dir')

with open('0.yaml') as f:
    data = yaml.load(f, Loader=yaml.FullLoader)

print(data)

假设我们有这样的YAML文件:

├── 0.yaml
└── include.d
    ├── 1.yaml
    └── 2.yaml

1.Yaml的内容:

name: "1"

2.Yaml的内容:

name: "2"

按名称包含文件

顶层: 如果0。yaml是:

!include include.d/1.yaml

我们会得到:

{"name": "1"}

在映射: 如果0。yaml是:

file1: !include include.d/1.yaml
file2: !include include.d/2.yaml

我们会得到:

  file1:
    name: "1"
  file2:
    name: "2"

在序列: 如果0。yaml是:

files:
  - !include include.d/1.yaml
  - !include include.d/2.yaml

我们会得到:

files:
  - name: "1"
  - name: "2"

ℹ注意: 文件名可以是绝对的(如/usr/conf/1.5/ make .yml)或相对的(如../../cfg/img.yml)。

通过通配符包含文件

文件名可以包含shell样式的通配符。从通配符找到的文件中加载的数据将按顺序设置。

如果0。yaml是:

files: !include include.d/*.yaml

我们会得到:

files:
  - name: "1"
  - name: "2"

ℹ注意: 对于Python>=3.5,如果递归参数!include YAML标记为真,模式" ** "将匹配任何文件和零个或多个目录和子目录。 在大型目录树中使用“**”模式可能会因为递归搜索而消耗过多的时间。

为了启用递归参数,我们应该在映射或序列模式下编写!include标记:

序列模式参数:

!include [tests/data/include.d/**/*.yaml, true]

映射模式参数说明

!include {pathname: tests/data/include.d/**/*.yaml, recursive: true}

我认为@max - b使用的解决方案看起来很棒。但是,对于嵌套的包含,它没有成功。例如,如果config_1。Yaml包含config_2。Yaml,其中包括config_3。Yaml,装弹机有问题。但是,如果您在加载时简单地将新的加载器类指向它自己,它就可以工作!具体来说,如果我们将旧的_include函数替换为稍微修改过的版本:

def _include(self, loader, node):                                    
     oldRoot = self.root                                              
     filename = os.path.join(self.root, loader.construct_scalar(node))
     self.root = os.path.dirname(filename)                           
     data = yaml.load(open(filename, 'r'), loader = IncludeLoader)                            
     self.root = oldRoot                                              
     return data

经过反思,我同意其他评论,嵌套加载一般不适合yaml,因为输入流可能不是一个文件,但它非常有用!

据我所知,YAML中不直接支持include,你必须自己提供一种机制,然而,这通常很容易做到。

我在我的python应用程序中使用YAML作为配置语言,在这种情况下经常定义这样的约定:

>>> main.yml <<<
includes: [ wibble.yml, wobble.yml]

然后在我的(python)代码中:

import yaml
cfg = yaml.load(open("main.yml"))
for inc in cfg.get("includes", []):
   cfg.update(yaml.load(open(inc)))

唯一的缺点是include中的变量将总是覆盖main中的变量,并且没有办法通过改变“includes:”语句在main中出现的位置来改变优先级。yml文件。

稍微不同的一点是,YAML不支持include,因为它并不是像基于文件的标记那样专门设计的。如果你在AJAX请求的响应中得到一个包含,它意味着什么?