YAML ファイルを別のファイル内に含めるにはどうすればよいですか?
-
22-08-2019 - |
質問
したがって、「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)))
。文はmain.ymlファイルに表示されます。 唯一のマイナス面は、内の変数は、常にメインで変数を上書きします、そして「含む場合に変更することで、その優先順位を変更する方法がないということです
若干異なる点について、YAMLは、その本当にとして独占的に、ファイルベースのマークアップなどとして設計されていないとして含まれてサポートしていません。あなたはAJAX要求に応答してそれを得た場合は何を意味するかが含まれるのでしょうか?
Josh_Bodeの答え@上の拡大、ここyaml.Loader
の自己完結型のサブクラスであるという利点を持っている私自身のPyYAMLとソリューションは、です。これは、任意のモジュールレベルのグローバルに、または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
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
).
ワイルドカードによるファイルの組み込み
ファイル名にはシェルスタイルのワイルドカードを含めることができます。ワイルドカードで見つかったファイルからロードされたデータが順番に設定されます。
もし 0.yaml
だった:
files: !include include.d/*.yaml
以下を取得します:
files:
- name: "1"
- name: "2"
ℹ 注記:
- のために
Python>=3.5
, 、 もしrecursive
の議論!include
YAML タグはtrue
, 、 パターン“**”
は、任意のファイルと、0 個以上のディレクトリおよびサブディレクトリに一致します。- の使用
“**”
大規模なディレクトリ ツリー内のパターンは、再帰的な検索により膨大な時間を消費する可能性があります。
有効にするには recursive
引数を書きます。 !include
タグイン Mapping
または Sequence
モード:
- の引数
Sequence
モード:
!include [tests/data/include.d/**/*.yaml, true]
- の引数
Mapping
モード:
!include {pathname: tests/data/include.d/**/*.yaml, recursive: true}
残念ながら、YAMLが標準でこれを提供していません。
しかし、あなたはルビーを使用している場合、あなたはルビーYAMLライブラリを拡張することによって求めている機能を提供する宝石があります: https://github.com/entwanderer/yaml_extendする
私は、@ MAXY-Bで使用されるソリューションは偉大に見えると思います。しかし、それは、ネストされた介在物と私のために成功しませんでした。 config_1.yamlはconfig_3.yamlを含むconfig_2.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のために適切でないと、他のコメントに同意するが、それは非常に便利です!
多分これはあなたを刺激でき、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 1.2がネイティブにこの機能が含まれていません。それにも関わらず、多くの実装がそうするように、いくつかの拡張機能を提供しています。
私は、次の目標を達成するために、カスタムYAMLタグを作成することができますその(YAMLファイルを発する/解析するJavaライブラリ)Javaとsnakeyaml:1.24
でそれを達成する方法を提示(私は定義されたテストスイートをロードするためにそれを使用しています表示されますいくつかのYAMLファイルにしていることを私は)ターゲットtest:
ノードのために含まれるのは、リストとして働かせた。
# ... yaml prev stuff
tests: !include
- '1.hello-test-suite.yaml'
- '3.foo-test-suite.yaml'
- '2.bar-test-suite.yaml'
# ... more yaml document
ここ!include
タグを処理することができ1クラスのJavaです。ファイルをクラスパス(Mavenのリソースディレクトリ)から読み込まれます:
/**
* Custom YAML loader. It adds support to the custom !include tag which allows splitting a YAML file across several
* files for a better organization of YAML tests.
*/
@Slf4j // <-- This is a Lombok annotation to auto-generate logger
public class MyYamlLoader {
private static final Constructor CUSTOM_CONSTRUCTOR = new MyYamlConstructor();
private MyYamlLoader() {
}
/**
* Parse the only YAML document in a stream and produce the Java Map. It provides support for the custom !include
* YAML tag to split YAML contents across several files.
*/
public static Map<String, Object> load(InputStream inputStream) {
return new Yaml(CUSTOM_CONSTRUCTOR)
.load(inputStream);
}
/**
* Custom SnakeYAML constructor that registers custom tags.
*/
private static class MyYamlConstructor extends Constructor {
private static final String TAG_INCLUDE = "!include";
MyYamlConstructor() {
// Register custom tags
yamlConstructors.put(new Tag(TAG_INCLUDE), new IncludeConstruct());
}
/**
* The actual include tag construct.
*/
private static class IncludeConstruct implements Construct {
@Override
public Object construct(Node node) {
List<Node> inclusions = castToSequenceNode(node);
return parseInclusions(inclusions);
}
@Override
public void construct2ndStep(Node node, Object object) {
// do nothing
}
private List<Node> castToSequenceNode(Node node) {
try {
return ((SequenceNode) node).getValue();
} catch (ClassCastException e) {
throw new IllegalArgumentException(String.format("The !import value must be a sequence node, but " +
"'%s' found.", node));
}
}
private Object parseInclusions(List<Node> inclusions) {
List<InputStream> inputStreams = inputStreams(inclusions);
try (final SequenceInputStream sequencedInputStream =
new SequenceInputStream(Collections.enumeration(inputStreams))) {
return new Yaml(CUSTOM_CONSTRUCTOR)
.load(sequencedInputStream);
} catch (IOException e) {
log.error("Error closing the stream.", e);
return null;
}
}
private List<InputStream> inputStreams(List<Node> scalarNodes) {
return scalarNodes.stream()
.map(this::inputStream)
.collect(toList());
}
private InputStream inputStream(Node scalarNode) {
String filePath = castToScalarNode(scalarNode).getValue();
final InputStream is = getClass().getClassLoader().getResourceAsStream(filePath);
Assert.notNull(is, String.format("Resource file %s not found.", filePath));
return is;
}
private ScalarNode castToScalarNode(Node scalarNode) {
try {
return ((ScalarNode) scalarNode);
} catch (ClassCastException e) {
throw new IllegalArgumentException(String.format("The value must be a scalar node, but '%s' found" +
".", scalarNode));
}
}
}
}
}
と シンフォニー, 、yaml の処理により、間接的に yaml ファイルをネストできるようになります。秘訣は、 parameters
オプション。例えば:
共通.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"
おそらくそれは質問が尋ねられた時にはサポートされていませんが、あなたは一つに他のYAMLファイルをインポートすることができます:
imports: [/your_location_to_yaml_file/Util.area.yaml]
私はすべてのオンラインの参照を持っていないが、これは私のために働くもののます。