2017-08-16 31 views
0

YAMLファイルの特定のキーの行番号を取得する必要があります。YAMLを解析し、順序付けされたマップでも行番号を取得する

this answerは問題を解決しません。私はruamel.yamlを使用します。答えは順序付きマップでは機能しません。

print(data['key1'].lc.line) # output: 1 
print(data['key1']['key4'].lc.line) # output: 4 

しかし:

!!omapキーを除いて、行番号にアクセスすることを許可していません何

CommentedMap([('key1', CommentedOrderedMap([('key2', 'item2'), ('key3', 'item3'), ('key4', CommentedOrderedMap([('key5', 'item5'), ('key6', 'item6')]))]))]) 

#!/usr/bin/env python3 
# -*- coding: utf-8 -*- 

from ruamel import yaml 

data = yaml.round_trip_load(""" 
key1: !!omap 
    - key2: item2 
    - key3: item3 
    - key4: !!omap 
    - key5: item5 
    - key6: item6 
""") 

print(data) 

その結果、私はこれを取得

print(data['key1']['key2'].lc.line) # output: AttributeError: 'str' object has no attribute 'lc' 

実際、data['key1']['key2]strです。

私は回避策を見つけた:

#!/usr/bin/env python3 
# -*- coding: utf-8 -*- 

from ruamel import yaml 

DATA = yaml.round_trip_load(""" 
key1: !!omap 
    - key2: item2 
    - key3: item3 
    - key4: !!omap 
    - key5: item5 
    - key6: item6 
""") 


def get_line_nb(data): 
    if isinstance(data, dict): 
     offset = data.lc.line 
     for i, key in enumerate(data): 
      if isinstance(data[key], dict): 
       get_line_nb(data[key]) 
      else: 
       print('{}|{} found in line {}\n' 
         .format(key, data[key], offset + i + 1)) 


get_line_nb(DATA) 

出力:

key2|item2 found in line 2 

key3|item3 found in line 3 

key5|item5 found in line 5 

key6|item6 found in line 6 

が、これは少し "汚い" に見えます。それを行うより適切な方法がありますか?

EDIT:この回避策だけでなく、汚れているが、唯一の上記のような単純な場合のために働く、とすぐにネストされたリストは、この問題は、あなたがしていることではない方法

答えて

1

であるとして誤った結果が得られます!omapを使用していて、「通常の」マッピングのように行番号を与えるわけではありません。それは、あなたがprint(data['key1']['key4'].lc.line)key4が外側!omapのキーであるところ)を行うことから4を得るという事実から明らかであるはずです。

thisとして答えdata['key1']['key4']の値はコレクションアイテム(別の!omap)ですが、data['key1']['key2']の値ではありませんあなたがコレクションアイテム

にプロパティLCにアクセスすることができ、

を示し、コレクションアイテムですが、lc属性を格納するスロットを持たない組み込みのPython文字列です。

あなたが lc属性を受け入れ、その後で利用可能な行の情報を転送するように調整 __slots__と( scalarstring.pyのクラスのようなものを使用して、 RoundTripConstructorをサブクラス化する必要があり、文字列のような非コレクションに .lc属性を取得するにはその属性のノードとし、行を設定し、カラム情報:

import sys 
import ruamel.yaml 

yaml_str = """ 
key1: !!omap 
    - key2: item2 
    - key3: item3 
    - key4: !!omap 
    - key5: 'item5' 
    - key6: | 
     item6 
""" 

class Str(ruamel.yaml.scalarstring.ScalarString): 
    __slots__ = ('lc') 

    style = "" 

    def __new__(cls, value): 
     return ruamel.yaml.scalarstring.ScalarString.__new__(cls, value) 

class MyPreservedScalarString(ruamel.yaml.scalarstring.PreservedScalarString): 
    __slots__ = ('lc') 

class MyDoubleQuotedScalarString(ruamel.yaml.scalarstring.DoubleQuotedScalarString): 
    __slots__ = ('lc') 

class MySingleQuotedScalarString(ruamel.yaml.scalarstring.SingleQuotedScalarString): 
    __slots__ = ('lc') 

class MyConstructor(ruamel.yaml.constructor.RoundTripConstructor): 
    def construct_scalar(self, node): 
     # type: (Any) -> Any 
     if not isinstance(node, ruamel.yaml.nodes.ScalarNode): 
      raise ruamel.yaml.constructor.ConstructorError(
       None, None, 
       "expected a scalar node, but found %s" % node.id, 
       node.start_mark) 

     if node.style == '|' and isinstance(node.value, ruamel.yaml.compat.text_type): 
      ret_val = MyPreservedScalarString(node.value) 
     elif bool(self._preserve_quotes) and isinstance(node.value, ruamel.yaml.compat.text_type): 
      if node.style == "'": 
       ret_val = MySingleQuotedScalarString(node.value) 
      elif node.style == '"': 
       ret_val = MyDoubleQuotedScalarString(node.value) 
      else: 
       ret_val = Str(node.value) 
     else: 
      ret_val = Str(node.value) 
     ret_val.lc = ruamel.yaml.comments.LineCol() 
     ret_val.lc.line = node.start_mark.line 
     ret_val.lc.col = node.start_mark.column 
     return ret_val 


yaml = ruamel.yaml.YAML() 
yaml.Constructor = MyConstructor 

data = yaml.load(yaml_str) 
print(data['key1']['key4'].lc.line) 
print(data['key1']['key2'].lc.line) 
print(data['key1']['key4']['key6'].lc.line) 

リテラルスカラー文字列が|で始まるようprintへの最後の呼び出しの出力は、6であることに注意してください

dataもダンプしたい場合は、RepresenterにそれらのMy....タイプを認識させる必要があります。

+0

これは本当に複雑に見えますが、おそらく私は回避策に固執するでしょう。ありがとう! – zezollo

+0

私は行番号を得るための答えを更新しました。ダンピング(必要に応じて)をあなたに残すだけでなく、数値、boolおよび他のスカラーを実行します。 – Anthon

+0

優秀!これは私の回避策よりはるかに優れていますが、途中で他の入れ子リストがあるとすぐに正しく機能しませんでした。 – zezollo

関連する問題