yaml - 如何在另一个内部包含一个YAML文件?

我有两个YAML文件,"A "和"B ",我希望A的内容插入到B中,或者拼接到现有的数据结构中,就像一个数组,或作为元素的子元素,如某个哈希键的值。

这是可能的吗?

时间:

不,YAML不包括任何类型的"import "或"include "语句。

你的问题没有要求python解决方案,但是,这里是使用PyYAML

PyYAML允许你将自定义构造函数(比如,包括)附加到YAML加载程序,我已经包含了一个可以设置的root目录,以便该解决方案支持相对和绝对文件引用。

原始解决方案,不要使用

注意:下面是一个更好的基于类的解决方案。


import os.path
import yaml

root = os.path.curdir

def include(loader, node):
 """Include another YAML file."""

 global root

 old_root = root

 filename = os.path.join(root, loader.construct_scalar(node))
 root = os.path.split(filename)[0]

 data = yaml.load(open(filename, 'r'))

 root = old_root

 return data

yaml.add_constructor('!include', include)

一个示例:

foo.yaml


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

bar.yaml


- 3.6
- [1, 2, 3]

然后,就像加载正常文件一样加载文件,并且将"included "文件:


>>> filename = 'foo.yaml'
>>> root = os.path.split(filename)[0]
>>> data = yaml.load(open(filename, 'r'))
{'a': 1, 'b': [1.43, 543.55], 'c': [3.6, [1, 2, 3]]}

Class-Based解决方案

根据我的注释,这里有一个更好的基于类的解决方案,它避免了全局root变量。


import yaml
import os.path

class Loader(yaml.Loader):

 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)

现在可以使用以下命令加载文件:


>>> 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]]}

扩展@Josh_Bode's答案,这里是我自己的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 

在这里我使用了mako

a.txt

这是文件a的最上面部分
< %include file='b/> "
这是文件的底部部分

b.txt

这是文件b

test.py


from mako.template import Template
from mako.lookup import TemplateLookup
import os

directory = os.path.dirname( os.path.abspath( __file__ ) )
mylookup = TemplateLookup(directories=[directory])
mytemplate = Template(filename="a.txt", lookup=mylookup)
finalsrc = mytemplate.render()
# finalsrc can be treated as yaml or whatever you like

$ python test.py
这是文件a的最上面部分
这是文件b
这是文件的底部部分

...