同じ YAML ファイル内の他の場所から YAML の「設定」を参照するにはどうすればよいですか?
-
20-09-2019 - |
質問
次の YAML があります。
paths:
patha: /path/to/root/a
pathb: /path/to/root/b
pathc: /path/to/root/c
削除してこれを「正規化」するにはどうすればよいですか /path/to/root/
3 つのパスから取得し、それを独自の設定として次のように設定します。
paths:
root: /path/to/root/
patha: *root* + a
pathb: *root* + b
pathc: *root* + c
明らかにそれは無効です、私はただでっち上げただけです。実際の構文は何ですか?それはできるでしょうか?
解決
私はそれが可能だとは思いません。あなたは、「ノード」ではなく、それの一部を再利用することができます。
bill-to: &id001
given : Chris
family : Dumars
ship-to: *id001
この完全に有効なYAMLで、フィールドgiven
とfamily
はship-to
ブロックで再利用されています。あなたはスカラーノードを同じように再利用することはできますが、内部で何変更し、YAML内部からそれへのパスの最後の部分を追加することができます方法はありません。
私はroot
財産のアプリケーションを認識させると、相対絶対的ではないに見えるすべてのパスに追加することをお勧めします。
他のヒント
はい、カスタムタグを使用して。 !join
タグを作るPythonでの例では、アレイ内の文字列を結合する
import yaml
## define custom tag handler
def join(loader, node):
seq = loader.construct_sequence(node)
return ''.join([str(i) for i in seq])
## register the tag handler
yaml.add_constructor('!join', join)
## using your sample data
yaml.load("""
paths:
root: &BASE /path/to/root/
patha: !join [*BASE, a]
pathb: !join [*BASE, b]
pathc: !join [*BASE, c]
""")
どちらになります:
{
'paths': {
'patha': '/path/to/root/a',
'pathb': '/path/to/root/b',
'pathc': '/path/to/root/c',
'root': '/path/to/root/'
}
}
!join
への引数の配列がある限り、彼らは文字列に変換することができますので、!join [*a, "/", *b, "/", *c]
はあなたが期待するものを同じように、任意のデータ型の任意の数の要素を持つことができます。
これを見てする別の方法は、単純に別のフィールドを使用することです。
paths:
root_path: &root
val: /path/to/root/
patha: &a
root_path: *root
rel_path: a
pathb: &b
root_path: *root
rel_path: b
pathc: &c
root_path: *root
rel_path: c
YML 定義:
dir:
default: /home/data/in/
proj1: ${dir.default}p1
proj2: ${dir.default}p2
proj3: ${dir.default}p3
タイムリーフのどこか
<p th:utext='${@environment.getProperty("dir.default")}' />
<p th:utext='${@environment.getProperty("dir.proj1")}' />
出力:/home/data/in//home/data/in/p1
一部の言語では、あなたが例えば、別のライブラリを使用することができ、タンポンのの実装ですYAMLの取り扱い変数ます:
const tampax = require('tampax');
const yamlString = `
dude:
name: Arthur
weapon:
favorite: Excalibur
useless: knife
sentence: "{{dude.name}} use {{weapon.favorite}}. The goal is {{goal}}."`;
const r = tampax.yamlParseString(yamlString, { goal: 'to kill Mordred' });
console.log(r.sentence);
// output : "Arthur use Excalibur. The goal is to kill Mordred."
私はこの機能を実行するPackagistで利用可能なライブラリを作成、しました: https://packagist.org/packages/grasmash/yaml-expanderする
例YAMLファイル:
type: book
book:
title: Dune
author: Frank Herbert
copyright: ${book.author} 1965
protaganist: ${characters.0.name}
media:
- hardcover
characters:
- name: Paul Atreides
occupation: Kwisatz Haderach
aliases:
- Usul
- Muad'Dib
- The Preacher
- name: Duncan Idaho
occupation: Swordmaster
summary: ${book.title} by ${book.author}
product-name: ${${type}.title}
例ロジック:
// Parse a yaml string directly, expanding internal property references.
$yaml_string = file_get_contents("dune.yml");
$expanded = \Grasmash\YamlExpander\Expander::parse($yaml_string);
print_r($expanded);
結果として得られる配列:
array (
'type' => 'book',
'book' =>
array (
'title' => 'Dune',
'author' => 'Frank Herbert',
'copyright' => 'Frank Herbert 1965',
'protaganist' => 'Paul Atreides',
'media' =>
array (
0 => 'hardcover',
),
),
'characters' =>
array (
0 =>
array (
'name' => 'Paul Atreides',
'occupation' => 'Kwisatz Haderach',
'aliases' =>
array (
0 => 'Usul',
1 => 'Muad\'Dib',
2 => 'The Preacher',
),
),
1 =>
array (
'name' => 'Duncan Idaho',
'occupation' => 'Swordmaster',
),
),
'summary' => 'Dune by Frank Herbert',
);
あなたの例が無効であることは、のみです。あなたには、いくつかの他の非予約文字で*
を交換した場合、あなたは完全に合法YAMLで終わる(私は、彼らはほとんどいくつかの仕様の一部として使用されていないように、そのために非ASCII文字を使用する傾向がある):
paths:
root: /path/to/root/
patha: ♦root♦ + a
pathb: ♦root♦ + b
pathc: ♦root♦ + c
これはあなたのパーサが使用すると魔法の何かを展開しない言語でマッピングするための標準的な表現にロードされます。
これを行うには、次のPythonプログラムのようにローカルにデフォルトのオブジェクト・タイプを使用します:
# coding: utf-8
from __future__ import print_function
import ruamel.yaml as yaml
class Paths:
def __init__(self):
self.d = {}
def __repr__(self):
return repr(self.d).replace('ordereddict', 'Paths')
@staticmethod
def __yaml_in__(loader, data):
result = Paths()
loader.construct_mapping(data, result.d)
return result
@staticmethod
def __yaml_out__(dumper, self):
return dumper.represent_mapping('!Paths', self.d)
def __getitem__(self, key):
res = self.d[key]
return self.expand(res)
def expand(self, res):
try:
before, rest = res.split(u'♦', 1)
kw, rest = rest.split(u'♦ +', 1)
rest = rest.lstrip() # strip any spaces after "+"
# the lookup will throw the correct keyerror if kw is not found
# recursive call expand() on the tail if there are multiple
# parts to replace
return before + self.d[kw] + self.expand(rest)
except ValueError:
return res
yaml_str = """\
paths: !Paths
root: /path/to/root/
patha: ♦root♦ + a
pathb: ♦root♦ + b
pathc: ♦root♦ + c
"""
loader = yaml.RoundTripLoader
loader.add_constructor('!Paths', Paths.__yaml_in__)
paths = yaml.load(yaml_str, Loader=yaml.RoundTripLoader)['paths']
for k in ['root', 'pathc']:
print(u'{} -> {}'.format(k, paths[k]))
これは印刷されます:
root -> /path/to/root/
pathc -> /path/to/root/c
エキスパンドはその場で行われ、ネストされた定義を処理していますが、無限再帰を呼び出すないことについて注意する必要がありますされます。
ダンパーを指定することで、あなたはので、オンザフライ拡張のため、中にロードされたデータから元のYAMLをダンプすることができます:
dumper = yaml.RoundTripDumper
dumper.add_representer(Paths, Paths.__yaml_out__)
print(yaml.dump(paths, Dumper=dumper, allow_unicode=True))
このマッピングキーの順序を変更します。それはあなたが持っている問題がある場合
(self.d
から輸入)CommentedMap
ruamel.yaml.comments.py
を作るために、
私は Python で独自のライブラリを作成し、次のような階層を持つディレクトリからロードされる変数を展開しました。
/root
|
+- /proj1
|
+- config.yaml
|
+- /proj2
|
+- config.yaml
|
... and so on ...
ここでの主な違いは、拡張はすべての処理が完了した後にのみ適用する必要があることです。 config.yaml
ファイルがロードされます。次のファイルの変数は前のファイルの変数をオーバーライドできるため、疑似コードは次のようになります。
env = YamlEnv()
env.load('/root/proj1/config.yaml')
env.load('/root/proj1/proj2/config.yaml')
...
env.expand()
追加オプションとして、 xonsh
スクリプトは、結果の変数を環境変数にエクスポートできます ( yaml_update_global_vars
関数)。
スクリプト:
https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Scripts/Tools/cmdoplib.yaml.py https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Scripts/Tools/cmdoplib.yaml.xsh
長所:
- 単純ですが、再帰とネストされた変数はサポートされていません
- 未定義の変数をプレースホルダーに置き換えることができます (
${MYUNDEFINEDVAR}
->*$/{MYUNDEFINEDVAR}
) - 環境変数から参照を展開できます (
${env:MYVAR}
) - すべてを置き換えることができます
\\
に/
パス変数内 (${env:MYVAR:path}
)
短所:
- ネストされた変数はサポートされていないため、ネストされた辞書内の値を展開できません (次のようなもの)
${MYSCOPE.MYVAR}
実装されていない) - プレースホルダー put 後の再帰を含む、展開再帰を検出しません。