разбирать YAML с номерами строк для всех ключей - PullRequest
0 голосов
/ 15 января 2020

Я хочу проанализировать файл YAML, структурированный следующим образом:

key1:
    key2: 10
    key3:
        key4:
            key5:
                "value1"
                "value2"

Сейчас я использую следующий код:

data = yaml.load(file, Loader=ruamel.yaml.RoundTripLoader)
print(data['key1'].lc.line) #I get line number of key1
print(data['key1']['key3'].lc.line) #I get line number of key3
print(data['key1']['key3']['key4'].lc.line) #I get line number of key4

Теперь я не могу получить номера строк для key2 и key5. Я понял, что проблема связана с тем, что data[key][key2] это не словарь, это скорее int. Точно так же data[key1][key3][key4][key5] - это список, а не словарь.

Есть ли способ получить номера строк для всех ключей в этом случае?

Ответы [ 3 ]

0 голосов
/ 16 января 2020

Это неправильно на нескольких уровнях:

  • Вы используете старый интерфейс ruamel.yaml и в вашем коде есть и yaml., и ruamel.yaml. Используете ли вы RoundTripLoader с PyYAML?

  • Ваш ввод недействителен YAML, так как вы не можете иметь скаляр в двойных кавычках ("value1"), за которым следует другой (вы можете, конечно, в Python). Поскольку вы говорите о «является списком», но на входе нет последовательности, которая могла бы создать список, вы, вероятно, не указали тире.

  • Вы путаете числа, которые получаете с номерами строк для ключей в ваших различных сопоставлениях. Хотя может быть трудно увидеть, что отсчет начинается с 0, очевидно, если вы введете дополнительную (пустую) строку в поле ввода

  • Вполне возможно напечатать data['key1']['key3']['key4']['key5'].lc.line , поскольку это значение является списком (подтипом), хотя это, конечно, номер строки для начала этого списка, а не для key5

  • , поскольку data['key1']['key2'] является int Я не понимаю, как вы можете ожидать атрибута, указывающего номер строки для этого типа (для себя или некоторого ключа, если это int, оказывается, значение dict). Вы когда-нибудь пытались установить атрибут для встроенного класса, например int? Попробуйте 10.lc = 24 иногда.


import ruamel.yaml 

yaml_str = """\
key1:
    key2: 10
    key3:

        key4:
            key5:
                - "value1"
                - "value2"
"""

yaml = ruamel.yaml.YAML()
data = yaml.load(yaml_str)
assert isinstance(data['key1']['key2'], int)
print(data['key1'].lc.line) # the starting line number of the value associated with of key1
print(data['key1']['key3'].lc.line) # the starting line number of the value associated with key3
print(data['key1']['key3']['key4'].lc.line) # the starting line number of the value of key4
print(data['key1']['key3']['key4']['key5'].lc.line) # the starting line number of the value of key5

, что дает:

1
4
5
6

Из-за дополнительной строки на входе выходные данные изменяются с 1 3 4 5 на 1 4 5 6, и должно быть ясно, что то, что вы не получаете, не является номерами строк ни для одного из ключей.

Таким образом, должно быть очевидно, что вы не получаете строку цифры для ключей вообще .

Да, есть способ получить номера строк для всех ключей.
(Если вы понимаете, что хотите знать, как это сделать: вы следует взять эту информацию из всех key_node присваиваний в методе construct_mapping класса RoundTripLoader (это подкласс и предоставить ее экземпляру yaml перед загрузкой) и присоединить ее к некоторому собственному атрибуту в maptyp переменная этого метода.)

0 голосов
/ 16 января 2020
key1:
  key4: 10
  key3:
    key8:

      key5:
        - "value1"
        - "value2"
      key6: 20
      key7:

        key2:
          - "value3"
          - "value4"
with open('test.yml') as f:
    lines = list(map(lambda x: x.strip().split(':')[0] if x.find(':') != -1 else "", f.readlines()))
    print(lines)
['key1', 'key4', 'key3', 'key8', '', 'key5', '', '', 'key6', 'key7', '', 'key2', '', '']

Здесь каждый ключ хранится по индексу его появления в файле yml

0 голосов
/ 15 января 2020
key1:
  key2: 10
  key3:
    key4:
      key5:
        - "value1"
        - "value2"
      key6: 20
      key7:
        key8:
          - "value3"
          - "value4"
import yaml

with open('test.yml') as f:
    data = yaml.full_load(f)

result = {}
current = 1


def keyId(val, current):
    ks = val.keys()
    for key in ks:
        result[key] = current
        current += 1
        if isinstance(val[key], type([])):
            current += len(val[key])
        if isinstance(val[key], type({})):
            keyId(val[key], current)

    return


keyId(data, current)
print(result)
{'key1': 1, 'key2': 2, 'key3': 3, 'key4': 4, 'key5': 5, 'key6': 8, 'key7': 9, 'key8': 10}
...