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

StackOverflow https://stackoverflow.com/questions/528281

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

私はすべてのオンラインの参照を持っていないが、これは私のために働くもののます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top