Frage

Also ich habe zwei YAML-Dateien, „A“ und „B“ und ich mag, dass die Inhalte von A nach innen B eingeführt werden, entweder gespleißt in die bestehenden Datenstruktur, wie ein Array oder als Kind eines Elements, wie der Wert für eine bestimmte Raute-Taste.

Ist das überhaupt möglich? Wie? Wenn nicht, alle Hinweise auf eine normative Referenz?

War es hilfreich?

Lösung

Nein, YAML enthält keine Art von "Import" oder "umfasst" Anweisung.

Andere Tipps

Ihre Frage nicht für eine Python-Lösung, aber hier ist man mit PyYAML .

PyYAML können Sie benutzerdefinierte Konstrukteure (wie !include) mit dem YAML Lader befestigen. Ich habe ein Root-Verzeichnis enthält, die so eingestellt werden können, dass diese Lösung relativen und absoluten Dateiverweis unterstützt.

Klasse-basierte Lösung

Hier ist eine Klasse-basierte Lösung, die die globale Wurzel Variable meiner ursprünglichen Antwort vermeidet.

Sehen Sie diese Kern für eine ähnliche, robustere Python 3-Lösung, die eine Metaklasse verwendet registrieren der benutzerdefinierten Konstruktor.

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)

Ein Beispiel:

foo.yaml

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

bar.yaml

- 3.6
- [1, 2, 3]

Nun können die Dateien geladen werden:

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

Wenn Sie Symfony-Version von YAML verwenden, ist dies möglich, wie folgt aus:

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

Enthält nicht direkt in YAML unterstützt, soweit ich weiß, werden Sie einen Mechanismus bereitstellen, müssen jedoch selbst, dies in der Regel einfach zu tun.

habe ich YAML als Konfigurationssprache in meine Python-Anwendungen verwendet wird, und in diesem Fall definieren oft eine Konvention wie folgt aus:

>>> main.yml <<<
includes: [ wibble.yml, wobble.yml]

Da ist in meinem (Python) Code, den ich tun:

import yaml
cfg = yaml.load(open("main.yml"))
for inc in cfg.get("includes", []):
   cfg.update(yaml.load(open(inc)))

Der einzige Nachteil ist, dass Variablen in der beinhaltet immer die Variablen in Haupt außer Kraft gesetzt, und es gibt keine Möglichkeit, diesen Vorrang zu ändern, indem, wo die „enthält: Anweisung erscheint in der main.yml Datei

.

Auf einer etwas anderen Stelle nicht YAML nicht unterstützt schließt als nicht wirklich entworfen wie ausschließlich als dateibasierte Auszeichnungs. Was wäre ein umfassen Mittel, wenn Sie es in einer Antwort auf eine AJAX-Anforderung bekommen?

Als Erweiterung @ Josh_Bode Antwort, hier ist meine eigene PyYAML Lösung, die den Vorteil hat, eine in sich geschlossene Unterklasse von yaml.Loader zu sein. Es hängt nicht von irgendwelchen Modulebene Globals oder den globalen Zustand des yaml Moduls zu ändern.

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                                                      

Für Python-Benutzer, können Sie versuchen, pyyaml-umfassen .

Installieren

pip install pyyaml-include

Verwendung

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)

Betrachten wir haben solche YAML Dateien:

├── 0.yaml
└── include.d
    ├── 1.yaml
    └── 2.yaml
  • 1.yaml ‚s Inhalt:
name: "1"
  • 2.yaml ‚s Inhalt:
name: "2"

Include-Dateien mit Namen

  • Auf Ebene:

    Wenn 0.yaml war:

!include include.d/1.yaml

Wir werden erhalten:

{"name": "1"}
  • In Abbildung:

    Wenn 0.yaml war:

file1: !include include.d/1.yaml
file2: !include include.d/2.yaml

Wir werden erhalten:

  file1:
    name: "1"
  file2:
    name: "2"
  • In der Reihenfolge:

    Wenn 0.yaml war:

files:
  - !include include.d/1.yaml
  - !include include.d/2.yaml

Wir werden erhalten:

files:
  - name: "1"
  - name: "2"
  

i Hinweis :

     

Dateiname kann entweder absolut (wie /usr/conf/1.5/Make.yml) oder relativ (wie ../../cfg/img.yml).

Include-Dateien durch Platzhalter

Dateiname kann Shell-artige Platzhalter enthalten. Daten aus der Datei geladen (n) durch Platzhalter gefunden werden in einer Folge eingestellt werden.

Wenn 0.yaml war:

files: !include include.d/*.yaml

Wir werden erhalten:

files:
  - name: "1"
  - name: "2"
  

i Hinweis :

     
      
  • Für Python>=3.5, wenn recursive Argument von !include YAML Tag ist true, wird das Muster “**” alle Dateien anzeigen lassen und Null oder mehr Verzeichnisse und Unterverzeichnisse.
  •   
  • Mit dem “**” Muster in großen Verzeichnisbäume können unmäßig viel Zeit wegen der rekursiven Suche verbrauchen.
  •   

Um recursive Argument zu ermöglichen, werden wir den !include Tag in Mapping oder Sequence Modus schreiben:

  • Argumente in Sequence Modus:
!include [tests/data/include.d/**/*.yaml, true]
  • Argumente in Mapping Modus:
!include {pathname: tests/data/include.d/**/*.yaml, recursive: true}

Leider ist dieses YAML bietet keine in seinem Standard.

Aber wenn Sie Ruby-verwenden, gibt es ein Juwel, die Funktionalität bereitstellt Sie fordern von der Rubin YAML Bibliothek erstreckt: https://github.com/entwanderer/yaml_extend

Ich denke, die Lösung, die von @ Maxy-B sieht gut aus. Allerdings ist es nicht für mich mit verschachtelten Einschlüsse gelingen. Zum Beispiel, wenn config_1.yaml umfasst config_2.yaml, die config_3.yaml umfasst es ein Problem mit dem Lader war. Wenn Sie jedoch einfach die neue Lader-Klasse selbst auf Lastpunkt, es funktioniert! Insbesondere dann, wenn wir die alte _include Funktion mit der sehr leicht modifizierte Version ersetzt werden:

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

Nach einigen Überlegungen bin ich mit den anderen Kommentaren, dass verschachtelter Laden für yaml im Allgemeinen nicht geeignet ist als der Eingangsstrom nicht eine Datei sein kann, aber es ist sehr nützlich!

Vielleicht inspirieren könnte, versuchen zu jbb Konventionen auszurichten:

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

Standard YAML 1.2 beinhaltet nicht nativ diese Funktion. Trotzdem viele Implementierungen einige Erweiterung bieten, dies zu tun.

stellt ich einen Weg, es mit Java zu erreichen und snakeyaml:1.24 (Java-Bibliothek zum Parsen / emittieren YAML-Dateien), die einen benutzerdefinierten YAML Tages ermöglicht die Erstellung das folgende Ziel zu erreichen (Sie werden sehen, ich verwende es Testsuiten zu laden definiert in mehreren YAML-Dateien und ich habe es als eine Liste arbeitet von für ein Ziel test: Knoten enthält):

# ... yaml prev stuff

tests: !include
  - '1.hello-test-suite.yaml'
  - '3.foo-test-suite.yaml'
  - '2.bar-test-suite.yaml'

# ... more yaml document

Hier ist die Ein-Klasse Java, die die !include Tag ermöglicht die Verarbeitung. Die Dateien werden geladen von Classpath (Maven Ressourcen-Verzeichnis):

/**
 * 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));
                }
            }
        }

    }

}

Mit Symfony , dessen Umgang mit yaml ermöglicht es Ihnen, indirekt zu nisten yaml Dateien. Der Trick ist die Verwendung der parameters Option zu machen. zB:

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%"

Das Ergebnis wird das gleiche sein wie:

whatever:
    thing:
        option: "value"
        foo:
            - "bar"
            - "baz"
    other_thing:
        option: "value"
        foo:
            - "bar"
            - "baz"

Wahrscheinlich war es nicht unterstützt, wenn Fragen gestellt wurden, aber Sie können andere YAML-Datei in eine importieren:

imports: [/your_location_to_yaml_file/Util.area.yaml]

Obwohl ich keine Online-Referenz haben, aber das funktioniert für mich.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top