453 Stimmen

Wie kann ich eine YAML-Datei in eine andere einfügen?

Also habe ich zwei YAML-Dateien, "A" und "B" und ich möchte den Inhalt von A in B einfügen, entweder in die vorhandene Datenstruktur eingeschnitten, wie ein Array, oder als Kind eines Elements, wie der Wert für einen bestimmten Hash-Key.

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

2 Stimmen

Ich bin kürzlich auf HiYaPyCo für Python gestoßen, das genau das macht. Sie können verschiedene YAML-Dateien zusammenführen. Es ist ein sehr schönes Python-Modul, das es wert ist, es zu kennen.

0 Stimmen

10voto

Maxy-B Punkte 2652

Um @Josh_Bodes Antwort zu erweitern, hier ist meine eigene PyYAML-Lösung, die den Vorteil hat, eine eigenständige Unterklasse von yaml.Loader zu sein. Es hängt nicht von globalen Modulvariablen ab oder modifiziert den globalen Zustand des yaml-Moduls.

import yaml, os

class IncludeLoader(yaml.Loader):                                                 
    """                                                                           
    yaml.Loader-Unterklasse behandelt "!include Pfad/zu/foo.yml" Direktiven in Konfigurationsdateien. Wenn sie mit einem Dateiobjekt konstruiert wird, werden die Standardpfade für Einschlüsse standardmäßig auf das Verzeichnis festgelegt, das die Datei enthält, andernfalls auf das aktuelle Arbeitsverzeichnis. In beiden Fällen kann der Grundpfad durch das Schlüsselwort `root` überschrieben werden.                                                      

    Wenn eine eingeschlossene Datei F ihre eigene !include-Direktive enthält, ist der Pfad relativ zur Position von F.                                                     

    Beispiel:                                                                      
        YAML-Datei /home/frodo/one-ring.yml:                                       
            ---                                                                   
            Name: Der Eine Ring                                                    
            Specials:                                                             
                - Größe an den Träger anpassen                                                
            Effekte: 
                - !include Pfad/zu/invisibility.yml                            

        YAML-Datei /home/frodo/Pfad/zu/invisibility.yml:                           
            ---                                                                   
            Name: Unsichtbarkeit                                                    
            Nachricht: Plötzlich verschwindest du!                                      

        Laden:                                                                  
            data = IncludeLoader(open('/home/frodo/one-ring.yml', 'r')).get_data()

        Ergebnis:                                                                   
            {'Effekte': [{'Nachricht': 'Plötzlich verschwindest du!', 'Name':            
                'Unsichtbarkeit'}], 'Name': 'Der Eine Ring', 'Specials':              
                ['Größe an den Träger anpassen']}                                             
    """                                                                           
    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

2 Stimmen

Schließlich bin ich dazu gekommen, den klassenbasierten Ansatz meiner Antwort hinzuzufügen, aber du hast mich geschlagen :) Hinweis: Wenn du yaml.load(f, IncludeLoader) innerhalb von _include verwendest, kannst du das Ersetzen des Wurzelelements vermeiden. Außerdem funktioniert die Lösung nicht mehr als eine Ebene tief, da die eingeschlossenen Daten die reguläre yaml.Loader-Klasse verwenden.

0 Stimmen

Ich musste das Schlüsselwort root von kwargs entfernen, nachdem ich self.root gesetzt hatte, um es mit Zeichenfolgen zum Laufen zu bringen. Ich habe den if-else-Block über den super Aufruf verschoben. Vielleicht kann jemand anderes meine Erkenntnis bestätigen oder mir zeigen, wie man die Klasse mit Zeichenfolgen und dem Parameter root verwendet.

1 Stimmen

Leider funktioniert dies nicht mit Referenzen wie ``` enthalten: &INKLUSIVE !include inner.yaml merge: <<: *INKLUSIVE ```

6voto

lbovet Punkte 183

Mit Yglu können Sie andere Dateien wie folgt importieren:

A.yaml

foo: !? $import('B.yaml')

B.yaml

bar: Hallo

$ yglu A.yaml
foo:
  bar: Hallo

Da $import eine Funktion ist, können Sie auch einen Ausdruck als Argument übergeben:

dep: !- b
foo: !? $import($_.dep.toUpper() + '.yaml')

Dies würde dasselbe Ergebnis wie oben erzielen.

Haftungsausschluss: Ich bin der Autor von Yglu.

0 Stimmen

Gut, nur eine Befehlszeile. Die einzige Lösung in diesem Thread, die kein Python-Programm erstellen muss, wenn Sie (hauptsächlich) kein Python nutzen möchten.

0 Stimmen

Ich habe Yglu für ein Projekt ausprobiert, das viele Sprachen behandelt, aber ich habe einige charmap: can't decode byte...-Fehler erhalten. Ich hinterlasse hier einen Kommentar, um anderen Leuten Zeit zu sparen.

3voto

Gerard Bosch Punkte 605

Standard YAML 1.2 enthält nativ nicht dieses Feature. Dennoch bieten viele Implementierungen einige Erweiterungen dafür.

Ich präsentiere eine Möglichkeit, dies mit Java und snakeyaml:1.24 (Java-Bibliothek zum Parsen/Erstellen von YAML-Dateien) zu erreichen, die es ermöglicht, einen benutzerdefinierten YAML-Tag zu erstellen, um das folgende Ziel zu erreichen (Sie werden sehen, dass ich es verwende, um Test-Suiten zu laden, die in mehreren YAML-Dateien definiert sind, und dass ich es als Liste von Inklusiven für einen Ziel-test:-Knoten funktionieren ließ):

# ... yaml vorherige Sachen

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

# ... mehr YAML-Dokument

Hier ist der Ein-Klassen-Java, der die Verarbeitung des !include-Tags ermöglicht. Dateien werden vom Klassenpfad geladen (Maven-Ressourcenverzeichnis):

/**
 * Benutzerdefinierter YAML-Loader. Er fügt Unterstützung für den benutzerdefinierten !include-Tag hinzu, der es ermöglicht, eine YAML-Datei auf mehrere Dateien aufzuteilen
 * für eine bessere Organisation von YAML-Tests.
 */
@Slf4j   // <-- Dies ist eine Lombok-Annotation zur automatischen Generierung eines Loggers
public class MyYamlLoader {

    private static final Constructor CUSTOM_CONSTRUCTOR = new MyYamlConstructor();

    private MyYamlLoader() {
    }

    /**
     * Analysiert das einzige YAML-Dokument in einem Stream und erstellt die Java-Map. Es bietet Unterstützung für den benutzerdefinierten !include
     * YAML Tag, um den YAML-Inhalt auf mehrere Dateien aufzuteilen.
     */
    public static Map load(InputStream inputStream) {
        return new Yaml(CUSTOM_CONSTRUCTOR)
                .load(inputStream);
    }

    /**
     * Benutzerdefinierter SnakeYAML-Konstruktor, der benutzerdefinierte Tags registriert.
     */
    private static class MyYamlConstructor extends Constructor {

        private static final String TAG_INCLUDE = "!include";

        MyYamlConstructor() {
            // Registriere benutzerdefinierte Tags
            yamlConstructors.put(new Tag(TAG_INCLUDE), new IncludeConstruct());
        }

        /**
         * Die eigentliche Include-Tag-Konstruktion.
         */
        private static class IncludeConstruct implements Construct {

            @Override
            public Object construct(Node node) {
                List inclusions = castToSequenceNode(node);
                return parseInclusions(inclusions);
            }

            @Override
            public void construct2ndStep(Node node, Object object) {
                // mache nichts
            }

            private List castToSequenceNode(Node node) {
                try {
                    return ((SequenceNode) node).getValue();

                } catch (ClassCastException e) {
                    throw new IllegalArgumentException(String.format("Der !import-Wert muss ein Sequenzknoten sein, aber " +
                            "'%s' gefunden wurde.", node));
                }
            }

            private Object parseInclusions(List inclusions) {

                List inputStreams = inputStreams(inclusions);

                try (final SequenceInputStream sequencedInputStream =
                             new SequenceInputStream(Collections.enumeration(inputStreams))) {

                    return new Yaml(CUSTOM_CONSTRUCTOR)
                            .load(sequencedInputStream);

                } catch (IOException e) {
                    log.error("Fehler beim Schließen des Streams.", e);
                    return null;
                }
            }

            private List inputStreams(List 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("Ressource-Datei %s nicht gefunden.", filePath));
                return is;
            }

            private ScalarNode castToScalarNode(Node scalarNode) {
                try {
                    return ((ScalarNode) scalarNode);

                } catch (ClassCastException e) {
                    throw new IllegalArgumentException(String.format("Der Wert muss ein Skalarknoten sein, aber '%s' gefunden wurde" +
                            ".", scalarNode));
                }
            }
        }

    }

}

0 Stimmen

Schön für einfache Fälle; Referenzen werden leider nicht aus den enthaltenen Dateien übernommen.

0 Stimmen

Hallo! Was meinst du mit "Verweise"? Meinst du transitive !includes? Wenn das ist, was du meinst, habe ich nicht daran gedacht. Aber ich denke, dass Unterstützung zur Lösung hinzugefügt werden könnte, indem load() rekursiv aufgerufen wird, bis keine !includes mehr übrig sind. Macht das Sinn?

0 Stimmen

Scheint, dass ich die korrekte Terminologie nicht verwendet habe: Anker und Aliase (bitbucket.org/asomov/snakeyaml/wiki/…) funktionieren nicht. Beim Blick auf die snakeyaml v1-Quelle wäre es ziemlich schwierig hinzuzufügen. Vielleicht ist v2 (auch bekannt als snakeyaml engine) etwas modularer ...

2voto

user8419486 Punkte 21

Leider bietet YAML dies nicht in seinem Standard.

Aber wenn Sie Ruby verwenden, gibt es ein Juwel, das die von Ihnen angeforderte Funktionalität bereitstellt, indem die Ruby YAML-Bibliothek erweitert wird: https://github.com/entwanderer/yaml_extend

2voto

Carson Punkte 3411

Ich mache einige Beispiele zur Referenz.

import yaml

main_yaml = """
Package:
 - !include _shape_yaml    
 - !include _path_yaml
"""

_shape_yaml = """
# Definieren
Rechteck: &id_Rectangle
    name: Rechteck
    breite: &Rectangle_width 20
    höhe: &Rectangle_height 10
    fläche: !product [*Rectangle_width, *Rectangle_height]

Kreis: &id_Circle
    name: Kreis
    radius: &Circle_radius 5
    fläche: !product [*Circle_radius, *Circle_radius, pi]

# Einstellen
Form:
    eigenschaft: *id_Rectangle
    farbe: rot
"""

_path_yaml = """
# Definieren
Root: &BASE /path/src/

Pfade: 
    a: &id_path_a !join [*BASE, a]
    b: &id_path_b !join [*BASE, b]

# Einstellen
Pfad:
    eingabedatei: *id_path_a
"""

# benutzerdefinierten Tag-Handler definieren
def yaml_import(loader, node):
    other_yaml_file = loader.construct_scalar(node)
    return yaml.load(eval(other_yaml_file), Loader=yaml.SafeLoader)

def yaml_product(loader, node):
    import math
    list_data = loader.construct_sequence(node)
    result = 1
    pi = math.pi
    for val in list_data:
        result *= eval(val) if isinstance(val, str) else val
    return result

def yaml_join(loader, node):
    seq = loader.construct_sequence(node)
    return ''.join([str(i) for i in seq])

def yaml_ref(loader, node):
    ref = loader.construct_sequence(node)
    return ref[0]

def yaml_dict_ref(loader: yaml.loader.SafeLoader, node):
    dict_data, key, const_value = loader.construct_sequence(node)
    return dict_data[key] + str(const_value)

def main():
    # den Tag-Handler registrieren
    yaml.SafeLoader.add_constructor(tag='!include', constructor=yaml_import)
    yaml.SafeLoader.add_constructor(tag='!product', constructor=yaml_product)
    yaml.SafeLoader.add_constructor(tag='!join', constructor=yaml_join)
    yaml.SafeLoader.add_constructor(tag='!ref', constructor=yaml_ref)
    yaml.SafeLoader.add_constructor(tag='!dict_ref', constructor=yaml_dict_ref)

    config = yaml.load(main_yaml, Loader=yaml.SafeLoader)

    pk_shape, pk_path = config['Package']
    pk_shape, pk_path = pk_shape['Form'], pk_path['Pfad']
    print(f"Form Name: {pk_shape['eigenschaft']['name']}")
    print(f"Form Fläche: {pk_shape['eigenschaft']['fläche']}")
    print(f"Form Farbe: {pk_shape['farbe']}")

    print(f"Eingabedatei: {pk_path['eingabedatei']}")

if __name__ == '__main__':
    main()

output

Form Name: Rechteck
Form Fläche: 200
Form Farbe: rot
Eingabedatei: /path/src/a

Update 2

und Sie können es kombinieren, wie folgt

# xxx.yaml
CREATE_FONT_PICTURE:
  PROJECTS:
    SUNG: &id_SUNG
      name: SUNG
      arbeitsverzeichnis: SUNG
      ausgabe_verzeichnis: temp
      schrift_pixel: 24

  DEFINIEREN: &id_define !ref [*id_SUNG]  # Sie können config['CREATE_FONT_PICTURE']['DEFINE'][name, work_dir, ... font_pixel] verwenden
  AUTO_INIT:
    dateinamensuffix: !dict_ref [*id_define, name, !product [5, 3, 2]]  # SUNG30

#  Dies ist nicht korrekt.
# basename_suffix: !dict_ref [*id_define, name, !product [5, 3, 2]]  # Es wird Deep-Level erstellt. id_define ist Deep-Level: 2. Daher müssen Sie es nach dem 2. einfügen. Andernfalls kann nicht auf den korrekten Wert verwiesen werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X