web-dev-qa-db-ja.com

YAMLファイルを別のファイルに含めるにはどうすればよいですか?

だから私は2つのYAMLファイル、「A」と「B」があり、Aの内容をBの内部に挿入し、配列のような既存のデータ構造に、または値のような要素の子として特定のハッシュキー用。

これはまったく可能ですか?どうやって?そうでない場合、規範的な参照へのポインタはありますか?

233
kch

いいえ、YAMLにはいかなる種類の「import」または「include」ステートメントも含まれません。

269
jameshfisher

あなたの質問はPythonソリューションを要求するものではありませんが、ここでは PyYAML を使用するものです。

PyYAMLでは、カスタムコンストラクター(!includeなど)をYAMLローダーにアタッチできます。このソリューションが相対および絶対ファイル参照をサポートするように設定できるルートディレクトリを含めました。

クラスベースのソリューション

これは、元の応答のグローバルルート変数を回避するクラスベースのソリューションです。

メタクラスを使用してカスタムコンストラクターを登録する同様の、より堅牢なPython 3ソリューションについては、この Gist を参照してください。

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]]}
90
Josh Bode

SymfonyのYAMLのバージョン を使用している場合、これは次のように可能です。

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

私が知っている限り、インクルードは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内の変数をオーバーライドすることです。また、main.ymlファイル内の「includes:ステートメント」の場所を変更して優先順位を変更する方法はありません。

わずかに異なる点で、YAMLはインクルードをサポートしていません。実際には、ファイルベースのマークアップとして排他的に設計されていません。 AJAXリクエストへの応答で受け取った場合、includeはどういう意味ですか?

10
clh

@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                                                      
7
Maxy-B

@ 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には適切ではないという他のコメントに同意しますが、非常に便利です!

1
PaddyM

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の場合、!includeYAML タグのrecursive引数がtrueである場合、パターン“**”は任意のファイルと0個以上のディレクトリおよびサブディレクトリに一致します。
  • 大きなディレクトリツリーで“**”パターンを使用すると、再帰的な検索のために膨大な時間がかかる場合があります。

recursive引数を有効にするには、MappingまたはSequenceモードで!includeタグを記述します。

  • Sequenceモードの引数:
!include [tests/data/include.d/**/*.yaml, true]
  • Mappingモードの引数:
!include {pathname: tests/data/include.d/**/*.yaml, recursive: true}
1
xqliang

残念ながら、YAMLは標準でこれを提供していません。

ただし、Rubyを使用している場合、Ruby YAMLライブラリを拡張することで、求めている機能を提供するgemがあります。 https://github.com/entwanderer/yaml_extend

1
user8419486

たぶん、これはあなたに刺激を与える可能性があります。

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

0
RzR

Symfonyを使用すると、yamlを処理すると、間接的にyamlファイルをネストできます。秘Theは、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"
0
jxmallett

標準のYAML 1.2には、この機能がネイティブに含まれていません。それにもかかわらず、多くの実装は、そうするための何らかの拡張を提供します。

Javaおよびsnakeyaml:1.24(YAMLファイルを解析/送信するJavaライブラリ)でそれを達成する方法を提示します。これにより、カスタムYAMLタグを作成して次の目標を達成できます(これを使用して、いくつかの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));
                }
            }
        }

    }

}
0
Gerard Bosch