Разбор YAML, возврат с номером строки
Я создаю генератор документов из данных YAML, который будет определять, из какой строки файла YAML генерируется каждый элемент. Каков наилучший способ сделать это? Поэтому, если файл YAML выглядит следующим образом:
- key1: item 1
key2: item 2
- key1: another item 1
key2: another item 2
Мне нужно что-то вроде этого:
[
{'__line__': 1, 'key1': 'item 1', 'key2': 'item 2'},
{'__line__': 3, 'key1': 'another item 1', 'key2': 'another item 2'},
]
В настоящее время я использую PyYAML, но любая другая библиотека в порядке, если я могу использовать его с Python.
Ответы
Ответ 1
Я сделал это, добавив крючки в Composer.compose_node
и Constructor.construct_mapping
:
import yaml
from yaml.composer import Composer
from yaml.constructor import Constructor
def main():
loader = yaml.Loader(open('data.yml').read())
def compose_node(parent, index):
# the line number where the previous token has ended (plus empty lines)
line = loader.line
node = Composer.compose_node(loader, parent, index)
node.__line__ = line + 1
return node
def construct_mapping(node, deep=False):
mapping = Constructor.construct_mapping(loader, node, deep=deep)
mapping['__line__'] = node.__line__
return mapping
loader.compose_node = compose_node
loader.construct_mapping = construct_mapping
data = loader.get_single_data()
print(data)
Ответ 2
Если вы используете ruamel.yaml >= 0.9 (из которых я являюсь автором), и используйте RoundTripLoader
, вы может получить доступ к свойству lc
для элементов коллекции, чтобы получить строку и столбец, где они были запущены в исходном YAML:
def test_item_04(self):
data = load("""
# testing line and column based on SO
# http://stackoverflow.com/questions/13319067/
- key1: item 1
key2: item 2
- key3: another item 1
key4: another item 2
""")
assert data[0].lc.line == 2
assert data[0].lc.col == 2
assert data[1].lc.line == 4
assert data[1].lc.col == 2
(подсчет строк и столбцов начинается с 0).
Этот ответ показывает, как добавить атрибут lc
к строковым типам во время загрузки.
Ответ 3
Для дальнейшего вдохновения, здесь мой код для этого. Он содержит больше информации, чем указано выше, когда он сообщает информацию о местоположении, используя start_mark, end_mark для каждого файла dict/list/unicode (с использованием подклассов dict_node, list_node, unicode_node, соответственно).
https://gist.github.com/dagss/5008118
Ответ 4
Вот улучшенная версия пазл-ответа:
import yaml
from yaml.loader import SafeLoader
class SafeLineLoader(SafeLoader):
def construct_mapping(self, node, deep=False):
mapping = super(SafeLineLoader, self).construct_mapping(node, deep=deep)
# Add 1 so line numbering starts at 1
mapping['__line__'] = node.start_mark.line + 1
return mapping
Вы можете использовать это так:
data = yaml.load(whatever, Loader=SafeLineLoader)