2012-11-10 19 views
13

Robię generator dokumentów z danych YAML, który określa, z której linii pliku YAML jest generowany każdy element. Jaki jest najlepszy sposób na zrobienie tego? Więc jeśli plik YAML jest tak:Parsowanie YAML, powrót z numerem linii

- key1: item 1 
    key2: item 2 
- key1: another item 1 
    key2: another item 2 

chcę coś takiego:

[ 
    {'__line__': 1, 'key1': 'item 1', 'key2': 'item 2'}, 
    {'__line__': 3, 'key1': 'another item 1', 'key2': 'another item 2'}, 
] 

obecnie używam PyYAML, ale każda inna biblioteka jest OK, jeśli można go używać z poziomu Pythona.

Odpowiedz

10

Zrobiłem go poprzez dodanie haki do Composer.compose_node i 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) 
+0

Dzięki - działało to doskonale i jest bardzo przydatne, jeśli chodzi o zgłaszanie błędów. –

2

W celu uzyskania dalszych inspiracji, tu jest mój kod do tego. Zawiera więcej informacji niż wymagane powyżej, ponieważ zgłasza informacje o położeniu za pomocą start_mark, end_mark na każdym dict/list/unicode (odpowiednio za pomocą dict_node, list_node, subklasses unicode_node).

https://gist.github.com/dagss/5008118

3

Jeśli używasz ruamel.yaml> = 0,9 (z których jestem autorem) i użyć RoundTripLoader, można uzyskać dostęp do właściwości lc na przedmioty zbiórki uzyskać wiersz i kolumnę, gdzie rozpoczęła się w źródło 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 

(liczba linii i kolumna liczona od 0).

This answer Pokaż, jak dodać atrybut lc do typów ciągów podczas ładowania.

+0

Nie można znaleźć sposobu, aby to działało, jeśli lista znajduje się wewnątrz uporządkowanej mapy, jak w 'key1:! Omap \ n - key4: item2 \ n - key3: item3' nie można uzyskać dostępu do' key4' oraz numery linii "key3'. – zezollo

+0

@zezollo i uporządkowana mapa domyślnie nie jest ładowana do struktury CommentedMap i dlatego nie ma atrybutu "lc". Będziesz musiał zarejestrować ładowanie! Omap jako podklasę CommentedMap. Jest to wykonalne, ale więcej niż mogę odpowiedzieć w komentarzu. Powinieneś zamieścić nowe pytanie, jeśli nie wiesz, jak to zrobić. – Anthon

+0

Rzeczywiście, nie mogę tego rozgryźć. Znalazłem tylko "brudne" obejście, aby uzyskać numery linii. Pytanie zadane [tutaj] (https://stackoverflow.com/questions/45716281/parsing-yaml-get-line-numbers-even-in-ordered-maps). – zezollo