したがって、「A」と「B」の 2 つの YAML ファイルがあり、A のコンテンツを B の中に挿入して、配列のように既存のデータ構造に結合するか、値のように要素の子として挿入する必要があります。特定のハッシュキーに対して。
これはまったく可能ですか?どのように?そうでない場合、規範的な参照へのポインタはありますか?
したがって、「A」と「B」の 2 つの YAML ファイルがあり、A のコンテンツを B の中に挿入して、配列のように既存のデータ構造に結合するか、値のように要素の子として挿入する必要があります。特定のハッシュキーに対して。
これはまったく可能ですか?どのように?そうでない場合、規範的な参照へのポインタはありますか?
いいえ、YAML には「インポート」または「インクルード」ステートメントは含まれていません。
あなたの質問は 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で直接サポートされていません。メカニズムを自分で提供する必要がありますが、これは一般的に簡単に実行できます。
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)))
唯一の欠点は、includes内の変数が常にmain内の変数をオーバーライドすることであり、「includes:ステートメント」がmain.ymlファイルのどこにあるかを変更してその優先順位を変更する方法はありません。
少し異なる点で、YAMLは、ファイルベースのマークアップほど排他的に設計されていないため、インクルードをサポートしていません。AJAXリクエストへの応答でインクルードを取得した場合、インクルードはどういう意味ですか?
@ Josh_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