2017-05-03 9 views
2

YAMLファイルを読み込んで処理し、後でダンプするYAMLフィルタを作成します。PyYAML:yamlファイルを読み込んでダンプし、タグを保持する(!CustomTag)

それは(箱から出してうまくすでに動作している)任意のエイリアス解決する必要があります。

>>> yaml.dump(yaml.load(""" 
Foo: &bar 
    name: bar 
Foo2: 
    <<: *bar 
""")) 

'Foo: {name: bar}\nFoo2: {name: bar}\n' 

をしかし、それはまたのように、​​表現の任意の種類を保存しなければならない:

>>> yaml.dump(yaml.load("Name: !Foo bar ")) 
yaml.constructor.ConstructorError: could not determine a constructor for the tag '!Foo' in "<unicode string>", line 1, column 7: 
Name: !Foo bar 
    ^

私はpyYAML Errors on "!" in a stringを読んでいます。これは私が必要とするものに近いですが、 引用符で囲まれた文字列などのカスタムタグUTS、したがってそれはもうタグではありません。

>>> def default_ctor(loader, tag_suffix, node): 
... return tag_suffix + ' ' + node.value 

>>> yaml.add_multi_constructor('', default_ctor) 
>>> yaml.dump(yaml.load("Name: !Foo bar "), default_flow_style=False) 
"Name: '!Foo bar'\n" 

私はそこに欠けている多くはありませんが、どうなったと思いますか?どのようにタグを含むファイルを読み込んで後でダンプすることができますか?

+0

'yaml.load()'を使うのは安全ではないため、YAMLファイルを制御できれば誰でも任意のコードを実行でき、PyYAMLはその危険を警告しません。 – Anthon

答えて

3

default_ctor()は文字列(タグとスカラーの連結のみ)を返すので、これがダンプされます。そして、タグが!で始まるので、その文字列をスカラーにダンプすると、引用符が付けられます。

あなたは一般的にそのタイプのために(すなわちルーチンをダンプ)あなたは特別なタイプのもの(とない「正常な」Python文字列)を格納し、representerを提供する必要があり、タグと値を保持したい場合:

import sys 
import yaml 

yaml_str = """\ 
Name: !Foo bar 
Alt: !Bar foo 
""" 


class GenericScalar: 
    def __init__(self, value, tag, style=None): 
     self._value = value 
     self._tag = tag 
     self._style = style 

    @staticmethod 
    def to_yaml(dumper, data): 
     # data is a GenericScalar 
     return dumper.represent_scalar(data._tag, data._value, style=data._style) 


def default_constructor(loader, tag_suffix, node): 
    if isinstance(node, yaml.ScalarNode): 
     return GenericScalar(node.value, tag_suffix, style=node.style) 
    else: 
     raise NotImplementedError('Node: ' + str(type(node))) 


yaml.add_multi_constructor('', default_constructor, Loader=yaml.SafeLoader) 

yaml.add_representer(GenericScalar, GenericScalar.to_yaml, Dumper=yaml.SafeDumper) 

data = yaml.safe_load(yaml_str) 
yaml.safe_dump(data, sys.stdout, default_flow_style=False, allow_unicode=True) 

これは与える:

Alt: !Bar 'foo' 
Name: !Foo 'bar' 

注:

  • PyYAMLとのを使用するのは危険です。 を使用しないでください(私のコードが示すように)必要はありません。それを悪化させるのは、PyYAMLから何か危険があるというフィードバックはないということです。
  • PyYAMLは、ノードスタイルを保存している(または空の文字列を強制する)場合でも、引用符付きのタグを持つすべてのスカラーをダンプします。そのことを防ぐためには、ノードのシリアライゼーションをかなり深く掘り下げなければなりません。私は引用符が非常に頻繁に必要でないので、私のruamel.yamlパッケージでこれを修正するために取り組んできました。
  • アンカーとエイリアスが解決されません。 PyYAMLが何かをするにはスマートではなく、読み込み時にmerge keyを展開するだけです。 YAMLに通常の自己参照がある場合、ダンプされたYAMLにアンカーとエイリアスが付けられます。
  • あなたのノードの後のノードがスカラー以外のもの(つまりマッピングまたはシーケンス)であれば、上記のように正しくエラーが発生します。一般的にそれらをロード/ダンプすることも可能です。いくつかのタイプを追加し、elif isinstance(node, yaml.MappingNode)elif isinstance(node, yaml.SequenceNode)default_constructorを拡張するだけです。私はこれらのタイプを作成することにしました(それはdict respリストのように動作します)。もしあなたがそのルートに行くなら、それらを構築することは2段階のプロセスで行われる必要があることに気づくべきです。(yieldサブノードの値を入力してオブジェクトを埋める)、そうでなければ、自己参照構造(ノード内のエイリアス)を使用することはできません。
  • PyYAMLとは、あなたがコロンで終わるタグ!CustomTag:を持つことができます
  • マッピング内の要素の順序を保持しませんが、それは次のように非常に見えるように私は、​​を読み取る優しいそれそれほど人間ではない見つけますキーと値のペアをブロックスタイルマッピングで使用します。
関連する問題