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

488voto

jameshfisher Punkte 29105

Nein, im Standard-YAML gibt es keine Art von "import" oder "include" Anweisung.

12 Stimmen

Du könntest einen !include Handler erstellen.

11 Stimmen

@clarkevans keine Frage, aber dieses Konstrukt würde "außerhalb" der YAML-Sprache liegen.

2 Stimmen

Wenn Sie Rails verwenden, können Sie <%= 'fdsa fdsa' %> ERB-Syntax einfügen und es wird funktionieren.

163voto

Josh Bode Punkte 3173

Ihre Frage verlangt nicht nach einer Python-Lösung, aber hier ist eine mit PyYAML.

PyYAML ermöglicht es Ihnen, benutzerdefinierte Konstruktoren (wie !include) dem YAML-Loader anzuhängen. Ich habe ein Stammverzeichnis eingefügt, das festgelegt werden kann, damit diese Lösung relative und absolute Dateiverweise unterstützt.

Klassenbasierte Lösung

Hier ist eine klassenbasierte Lösung, die die globale Stammvariablen meiner ursprünglichen Antwort vermeidet.

Sehen Sie sich diesen gist für eine ähnliche, robustere Python 3-Lösung an, die eine Metaklasse verwendet, um den benutzerdefinierten Konstruktor zu registrieren.

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]

Die Dateien können jetzt geladen werden mit:

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

0 Stimmen

Dies ist ein interessantes Feature, danke. Aber was ist der Zweck all dieser Manipulationen mit root/old_root? Ich nehme an, der Code der include-Funktion kann vereinfacht werden: ` def include(loader, node): """Eine andere YAML-Datei einfügen.""" dateiname = loader.construct_scalar(node) daten = yaml.load(open(dateiname)) `

0 Stimmen

Der globale Stamm ist dort, damit relative Einschlüsse in beliebiger Tiefe funktionieren, z.B. wenn eingeschlossene Dateien in einem anderen Verzeichnis sitzen und eine Datei relativ zu diesem Verzeichnis einschließen. Absolute Einschlüsse sollten auch funktionieren. Es gibt wahrscheinlich eine sauberere Möglichkeit, dies ohne eine globale Variable zu tun, vielleicht durch Verwendung einer benutzerdefinierten yaml.Loader-Klasse.

2 Stimmen

Ist es auch möglich, so etwas zu haben: foo.yaml: a: bla bar.yaml: ` !include foo.yaml b: blubb` So dass das Ergebnis wäre: `{'a': bla, 'b': blubb}

24voto

xqliang Punkte 317

Für Python-Benutzer empfehlen sich pyyaml-include.

Install

pip install pyyaml-include

Verwendung

import yaml
from yamlinclude import YamlIncludeConstructor

YamlIncludeConstructor.add_to_loader_class(loader_class=yaml.FullLoader, base_dir='/dein/conf/verzeichnis')

with open('0.yaml') as f:
    data = yaml.load(f, Loader=yaml.FullLoader)

print(data)

Anzunehmen ist, dass solche YAML-Dateien existieren:

 0.yaml
 include.d
     1.yaml
     2.yaml
  • 1.yaml's Inhalt:

    name: "1"

  • 2.yaml's Inhalt:

    name: "2"

Dateien nach Namen einbinden

  • Auf oberster Ebene:

    Wenn 0.yaml folgendermaßen aussah:

    !include include.d/1.yaml

Ergebnis wäre:

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

    Wenn 0.yaml folgendermaßen aussah:

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

Ergebnis wäre:

  file1:
    name: "1"
  file2:
    name: "2"
  • In Sequenz:

    Wenn 0.yaml folgendermaßen aussah:

    files:

    • !include include.d/1.yaml
    • !include include.d/2.yaml

Ergebnis wäre:

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

Hinweis:

Dateinamen können entweder absolut (wie /usr/conf/1.5/Make.yml) oder relativ (wie ../../cfg/img.yml) sein.

Dateien nach Platzhaltern einbinden

Dateinamen können Platzhalter im Stil von Shell enthalten. Daten aus den gefundenen Dateien werden in Sequenzen gesetzt.

Wenn 0.yaml folgendermaßen aussah:

files: !include include.d/*.yaml

Ergebnis wäre:

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

Hinweis:

  • Für Python>=3.5, wenn das recursive Argument des !include YAML-Tags true ist, wird das Muster “**” auf alle Dateien und null oder mehr Verzeichnisse und Unterverzeichnisse zutreffen.
  • Die Verwendung des Musters “**” in großen Verzeichnisbäumen kann aufgrund der rekursiven Suche eine unverhältnismäßige Menge an Zeit in Anspruch nehmen.

Um das Argument recursive zu aktivieren, muss der !include-Tag im Mapping- oder Sequence-Modus geschrieben werden:

  • Argumente im Sequence-Modus:

    !include [tests/data/include.d/*/.yaml, true]

  • Argumente im Mapping-Modus:

    !include {pathname: tests/data/include.d/*/.yaml, recursive: true}

0 Stimmen

Das beantwortet die Frage tatsächlich nicht. Es betrifft eine Python-Lösung, nicht eine mit dem standardisierten YAML-Format.

2 Stimmen

@oligofren Die benutzerdefinierten Tag-Handler sind eine Funktion von YAML, die es Parsern ermöglicht, YAML zu erweitern, um Typen festzulegen und benutzerdefinierte Verhaltensweisen wie diese zu implementieren. Es wäre eine große Herausforderung für die YAML-Spezifikation selbst, so weit zu gehen, um vorzuschreiben, wie die Dateieinbindung mit allen unterschiedlichen Betriebssystempfadspezifikationen, Dateisystemen usw. funktionieren sollte.

0 Stimmen

@AntonStrogonoff Vielen Dank, dass du mich darauf aufmerksam gemacht hast. Könntest du mir zeigen, wo das im RFC steht? Dort wird das Wort "benutzerdefiniert" nicht erwähnt. Ref yaml.org/spec/1.2/spec.html

23voto

clh Punkte 809

Enthält werden in YAML, soweit ich weiß, nicht direkt unterstützt, du musst jedoch einen Mechanismus selbst bereitstellen, was jedoch im Allgemeinen einfach zu tun ist.

Ich habe YAML als Konfigurationssprache in meinen Python-Apps verwendet und definiere in diesem Fall oft eine Konvention wie diese:

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

Dann mache ich in meinem (Python-) Code Folgendes:

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 den includes immer die Variablen im Hauptteil überschreiben und es keine Möglichkeit gibt, diese Präzedenz zu ändern, indem man angibt, wo die "includes:" Anweisung in der main.yml-Datei erscheint.

An einer etwas anderen Stelle unterstützt YAML keine includes, da es nicht wirklich als ausschließlich dateibasierte Auszeichnung konzipiert ist. Was würde ein include bedeuten, wenn du es als Antwort auf eine AJAX-Anfrage bekommst?

5 Stimmen

Dies funktioniert nur, wenn die YAML-Datei keine verschachtelte Konfiguration enthält.

20voto

bvdb Punkte 18946

Der YML-Standard legt nicht fest, wie dies zu tun ist. Und dieses Problem beschränkt sich nicht nur auf YML. JSON hat die gleichen Einschränkungen.

Viele Anwendungen, die auf YML- oder JSON-basierten Konfigurationen basieren, stoßen letztendlich auf dieses Problem. Und wenn das passiert, erfinden sie ihre eigene Konvention.

z.B. für Swagger-API-Definitionen:

$ref: 'file.yml'

z.B. für Docker-Compose-Konfigurationen:

services:
  app:
    extends:
      file: docker-compose.base.yml

Alternativ, wenn Sie den Inhalt einer yml-Datei in mehrere Dateien aufteilen möchten, ähnlich einem Inhaltsbaum, können Sie Ihre eigene Ordnerstrukturkonvention definieren und ein (vorhandenes) Merge-Skript verwenden.

2 Stimmen

Dies sollte weiter oben stehen. Oftmals müssen YAML-Dateien in andere importiert werden, weil es sich um eine Konfigurationsdatei für ein bestimmtes Framework handelt. Es lohnt sich immer zu prüfen, ob das Framework selbst eine Möglichkeit bietet, dies zu tun, ohne das Rad neu zu erfinden.

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