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

所以我有两个YAML文件,“A”和“B”,我想A的内容被插入B的内部,无论是拼接到现有的数据结构,如阵列,或作为元素的子元素,像值对于某个哈希键。 这有可能吗?怎么样?如果没有,任何指向规范参考的指针?     
已邀请:
不,YAML不包含任何类型的“导入”或“包含”声明。     
您的问题不是要求Python解决方案,而是使用PyYAML。 PyYAML允许您将自定义构造函数(例如
!include
)附加到YAML加载程序。我已经包含了一个可以设置的根目录,以便此解决方案支持相对和绝对文件引用。 基于类的解决方案 这是一个基于类的解决方案,它避免了我原始响应的全局根变量。 请参阅此要点,了解类似的,更强大的Python 3解决方案,该解决方案使用元类来注册自定义构造函数。
import yaml
import os.path

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]]}
    
如果你使用Symfony的YAML版本,这是可能的,如下所示:
imports:
    - { resource: sub-directory/file.yml }
    - { resource: sub-directory/another-file.yml }
    
据我所知,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请求的响应中得到它,那么包含意味着什么?     
扩展@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                                                      
    
不幸的是,YAML没有在其标准中提供此功能。 但是如果您使用的是Ruby,那么有一个gem可以通过扩展ruby YAML库来提供您要求的功能: https://github.com/entwanderer/yaml_extend     
我认为@ maxy-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,因为输入流可能不是文件,但它非常有用!     
使用Symfony,它对yaml的处理将间接允许您嵌套yaml文件。诀窍是使用
parameters
选项。例如: common.yml
parameters:
    yaml_to_repeat:
        option: "value"
        foo:
            - "bar"
            - "baz"
config.yml
imports:
    - { resource: common.yml }
whatever:
    thing: "%yaml_to_repeat%"
    other_thing: "%yaml_to_repeat%"
结果将与:
whatever:
    thing:
        option: "value"
        foo:
            - "bar"
            - "baz"
    other_thing:
        option: "value"
        foo:
            - "bar"
            - "baz"
    
也许这可以激发你的灵感,尝试与jbb约定保持一致: https://docs.openstack.org/infra/jenkins-job-builder/definition.html#inclusion-tags
- job:
    name: test-job-include-raw-1
    builders:
      - shell:
          !include-raw: include-raw001-hello-world.sh
    
可能在询问问题时不支持,但您可以将其他YAML文件导入到一个:
imports: [/your_location_to_yaml_file/Util.area.yaml]
虽然我没有任何在线参考,但这对我有用。     

要回复问题请先登录注册