web-dev-qa-db-ja.com

複数のドキュメントを含むYAMLファイルを解析する方法は?

これが私の解析コードです:

import yaml

def yaml_as_python(val):
    """Convert YAML to dict"""
    try:
        return yaml.load_all(val)
    except yaml.YAMLError as exc:
        return exc

with open('circuits-small.yaml','r') as input_file:
    results = yaml_as_python(input_file)
    print results
    for value in results:
         print value

ファイルのサンプルは次のとおりです。

ingests:
  - timestamp: 1970-01-01T00:00:00.000Z
    id: SwitchBank_35496721
    attrs:
      Feeder: Line_928
      Switch.normalOpen: 'true'
      IdentifiedObject.description: SwitchBank
      IdentifiedObject.mRID: SwitchBank_35496721
      PowerSystemResource.circuit: '928'
      IdentifiedObject.name: SwitchBank_35496721
      IdentifiedObject.aliasName: SwitchBank_35496721
    loc: vector [43.05292, -76.126800000000003, 0.0]
    kind: SwitchBank
  - timestamp: 1970-01-01T00:00:00.000Z
    id: UndergroundDistributionLineSegment_34862802
    attrs:
      Feeder: Line_928
      status: de-energized
      IdentifiedObject.description: UndergroundDistributionLineSegment
      IdentifiedObject.mRID: UndergroundDistributionLineSegment_34862802
      PowerSystemResource.circuit: '928'
      IdentifiedObject.name: UndergroundDistributionLineSegment_34862802
    path:
    - vector [43.052942000000002, -76.126716000000002, 0.0]
    - vector [43.052585000000001, -76.126515999999995, 0.0]
    kind: UndergroundDistributionLineSegment
  - timestamp: 1970-01-01T00:00:00.000Z
    id: UndergroundDistributionLineSegment_34806014
    attrs:
      Feeder: Line_928
      status: de-energized
      IdentifiedObject.description: UndergroundDistributionLineSegment
      IdentifiedObject.mRID: UndergroundDistributionLineSegment_34806014
      PowerSystemResource.circuit: '928'
      IdentifiedObject.name: UndergroundDistributionLineSegment_34806014
    path:
    - vector [43.05292, -76.126800000000003, 0.0]
    - vector [43.052928999999999, -76.126766000000003, 0.0]
    - vector [43.052942000000002, -76.126716000000002, 0.0]
    kind: UndergroundDistributionLineSegment
... 
ingests:
  - timestamp: 1970-01-01T00:00:00.000Z
    id: OverheadDistributionLineSegment_31168454

トレースバックでは、...で問題が発生し始めることに注意してください。

Traceback (most recent call last):
  File "convert.py", line 29, in <module>
    for value in results:
  File "/Users/conduce-laptop/anaconda2/lib/python2.7/site-packages/yaml/__init__.py", line 82, in load_all
    while loader.check_data():
  File "/Users/conduce-laptop/anaconda2/lib/python2.7/site-packages/yaml/constructor.py", line 28, in check_data
    return self.check_node()
  File "/Users/conduce-laptop/anaconda2/lib/python2.7/site-packages/yaml/composer.py", line 18, in check_node
    if self.check_event(StreamStartEvent):
  File "/Users/conduce-laptop/anaconda2/lib/python2.7/site-packages/yaml/parser.py", line 98, in check_event
    self.current_event = self.state()
  File "/Users/conduce-laptop/anaconda2/lib/python2.7/site-packages/yaml/parser.py", line 174, in parse_document_start
    self.peek_token().start_mark)
yaml.parser.ParserError: expected '<document start>', but found '<block mapping start>'
  in "circuits-small.yaml", line 42, column 1

私が望んでいるのは、これらのドキュメントのそれぞれを個別のオブジェクトとして解析することです。おそらく、すべてが同じリストにあるか、PyYAMLモジュールで機能する他のほとんどすべてのものです。私は...は実際には有効なYAMLであるため、自動的に処理されないことに驚いています。

5
BigBoy1337

エラーメッセージは、ドキュメントが ドキュメント開始マーカー で始まる必要があることを非常に具体的に示しています。最初のドキュメントにはそのようなマーカーはありませんが、ドキュメントの終了マーカーがあります。最初のドキュメントを_..._で明示的に終了すると、PyYAMLでドキュメント境界マーカーのないドキュメントを使用できなくなります。明示的に_---_で開始する必要があります。

ファイルの終わりは次のようになります。

_    kind: UndergroundDistributionLineSegment
...
---
ingests:
  - timestamp: 1970-01-01T00:00:00.000Z
    id: OverheadDistributionLineSegment_31168454
_

最初のドキュメントから明示的なドキュメント開始マーカーを省略できますが、後続のすべてのドキュメントに開始マーカーを含める必要があります。ドキュメントの終了マーカーはオプションです。

入力を完全に制御できない場合、.load_all()を使用するのは安全ではありません。通常、そのリスクを冒す理由はありません。YAMLに含まれる可能性のある特定のタグを処理するには、.safe_load_all()を使用してSafeLoaderを拡張する必要があります。

それとは別に、YAMLドキュメントを明示的な バージョンディレクティブ でドキュメント開始インジケーターの前に開始する必要があります(これも最初のドキュメントに追加する必要があります):

_%YAML 1.1
---
_

これは、YAML 1.2仕様(フォーム2009)ではなくYAML 1.1(のほとんど)のみをサポートするPyYAMLを使用しているため、YAMLファイルの将来の編集者の利益のためです。もちろん、別の方法は、YAMLパーサーをたとえば ruamel.yaml にアップグレードすることです。これにより、安全でないload_all()の使用についても警告が表示されます(免責事項:私はそのパーサー)。 _ruamel.yaml_は、明示的なドキュメントの終わりマーカー(@flyxが指摘したように許可されます)の後にベアドキュメントを作成することを許可しません。これは バグ です。

7
Anthon

無効なyamlがあると思います

サンプルの2番目のドキュメントを見てください。---ではなく...で始まります。

... 
ingests:
  - timestamp: 1970-01-01T00:00:00.000Z
    id: OverheadDistributionLineSegment_31168454
1
gipsy