当前位置:  开发笔记 > 编程语言 > 正文

如何在另一个文件中包含YAML文件?

如何解决《如何在另一个文件中包含YAML文件?》经验,为你挑选了6个好方法。

所以我有两个YAML文件,"A"和"B",我想要将A的内容插入到B中,或者拼接到现有的数据结构中,像数组一样,或者作为元素的子元素,比如值对于某个哈希键.

这有可能吗?怎么样?如果没有,任何指向规范参考的指针?



1> jameshfisher..:

不,YAML不包含任何类型的"导入"或"包含"声明.


我认为该答案应改为:“不,标准YAML不包含此功能。尽管如此,许多实现都对此提供了一些扩展。”
您可以创建一个!include 处理程序.
@clarkevans肯定,但那个结构将在YAML语言"之外".

2> Josh Bode..:

您的问题不是要求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]]}


这应该是公认的答案.此外,安全挑剔,您应该使用yaml.safeload而不是yaml.load,以避免特制的yaml拥有您的服务.
是否也可以这样:foo.yaml:`a:bla` bar.yaml:`!include foo.yaml b:blubb`结果将是:`{'a':bla,'b ':blubb}

3> daveaspinall..:

如果你正在使用Symfony的YAML版本,这是可能的,如下所示:

imports:
    - { resource: sub-directory/file.yml }
    - { resource: sub-directory/another-file.yml }


这特定于Symfony如何解释YAML,而不是YAML本身的一部分.
是的,这就是我发布Symfony docs链接的原因.问题是"这有可能吗?怎么样?"......就是这样.看到没有理由进行downvote.
没有"Symfony版本的YAML"......这只是一个特定于供应商的YAML兼容库,它有额外的东西,不属于YAML.
我没有向你投票; 我只是指出这是针对Symfony YAML的.
如果"基于阶级"的答案被推翻,没有理由拒绝这个答案.

4> 小智..:

据我所知,YAML不直接支持包含,但您必须自己提供一种机制,这通常很容易实现.

我在我的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)))

唯一的缺点是包含中的变量将始终覆盖main中的变量,并且无法通过更改"includes:"语句出现在main.yml文件中的位置来更改该优先级.

在一个稍微不同的点上,YAML不支持包含,因为它的设计并不像基于文件的标记那样完全.如果你在回答一个AJAX请求时得到了什么意思?


这仅在yaml文件不包含嵌套配置时有效。

5> Maxy-B..:

扩展@Jos​​h_Bode的答案,这是我自己的PyYAML解决方案,它的优势在于它是一个自包含的子类yaml.Loader.它不依赖于任何模块级全局变量,也不依赖于修改yaml模块的全局状态.

import yaml, os

class IncludeLoader(yaml.Loader):                                                 
    """                                                                           
    yaml.Loader subclass handles "!include path/to/foo.yml" directives in config  
    files.  When constructed with a file object, the root path for includes       
    defaults to the directory containing the file, otherwise to the current       
    working directory. In either case, the root path can be overridden by the     
    `root` keyword argument.                                                      

    When an included file F contain its own !include directive, the path is       
    relative to F's location.                                                     

    Example:                                                                      
        YAML file /home/frodo/one-ring.yml:                                       
            ---                                                                   
            Name: The One Ring                                                    
            Specials:                                                             
                - resize-to-wearer                                                
            Effects: 
                - !include path/to/invisibility.yml                            

        YAML file /home/frodo/path/to/invisibility.yml:                           
            ---                                                                   
            Name: invisibility                                                    
            Message: Suddenly you disappear!                                      

        Loading:                                                                  
            data = IncludeLoader(open('/home/frodo/one-ring.yml', 'r')).get_data()

        Result:                                                                   
            {'Effects': [{'Message': 'Suddenly you disappear!', 'Name':            
                'invisibility'}], 'Name': 'The One Ring', 'Specials':              
                ['resize-to-wearer']}                                             
    """                                                                           
    def __init__(self, *args, **kwargs):                                          
        super(IncludeLoader, self).__init__(*args, **kwargs)                      
        self.add_constructor('!include', self._include)                           
        if 'root' in kwargs:                                                      
            self.root = kwargs['root']                                            
        elif isinstance(self.stream, file):                                       
            self.root = os.path.dirname(self.stream.name)                         
        else:                                                                     
            self.root = os.path.curdir                                            

    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'))                            
        self.root = oldRoot                                              
        return data                                                      


终于开始在我的答案中添加基于类的方法,但是你打败了我的注意力:)注意:如果在`_include`中使用`yaml.load(f,IncludeLoader)`,你可以避免必须替换root .此外,除非您这样做,否则解决方案将不会超过一个级别,因为包含的数据使用常规的`yaml.Loader`类.

6> xqliang..:

对于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,如果YAML标签的recursive参数为,则该模式将匹配任何文件以及零个或多个目录和子目录。!include true“**”

“**”由于递归搜索,在大型目录树中使用该模式可能会花费大量时间。

为了启用recursive参数,我们!include将以MappingSequence模式写入标记:

Sequence模式下的参数:

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

Mapping模式下的参数:

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

推荐阅读
wangtao
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有