Zawsze uważałem, że XML jest nieco kłopotliwy w przetwarzaniu. Nie mówię o implementacji analizatora składni XML: mówię o używaniu istniejącego analizatora opartego na strumieniu, takiego jak analizator składni SAX, który przetwarza węzeł XML według węzła.
Tak, naprawdę łatwo jest nauczyć się różnych interfejsów API dla tych parserów, ale kiedy patrzę na kod przetwarzający XML, zawsze uważam, że jest nieco skomplikowany. Zasadniczym problemem wydaje się być to, że dokument XML jest logicznie podzielony na poszczególne węzły, a jednak typy danych i atrybuty są często oddzielone od rzeczywistych danych, czasami przez wiele poziomów zagnieżdżenia. Dlatego podczas indywidualnego przetwarzania dowolnego węzła należy zachować wiele dodatkowych stanów, aby określić, gdzie jesteśmy i co musimy zrobić dalej.
Na przykład biorąc fragment kodu z typowego dokumentu XML:
<book>
<title>Blah blah</title>
<author>Blah blah</author>
<price>15 USD</price>
</book>
... Jak określić, kiedy napotkałem węzeł tekstowy zawierający tytuł książki? Załóżmy, że mamy prosty parser XML, który działa jak iterator, dając nam następny węzeł w dokumencie XML za każdym razem, gdy wywołujemy XMLParser.getNextNode()
. Nieuchronnie piszę następujący kod:
boolean insideBookNode = false;
boolean insideTitleNode = false;
while (!XMLParser.finished())
{
....
XMLNode n = XMLParser.getNextNode();
if (n.type() == XMLTextNode)
{
if (insideBookNode && insideTitleNode)
{
// We have a book title, so do something with it
}
}
else
{
if (n.type() == XMLStartTag)
{
if (n.name().equals("book")) insideBookNode = true
else if (n.name().equals("title")) insideTitleNode = true;
}
else if (n.type() == XMLEndTag)
{
if (n.name().equals("book")) insideBookNode = false;
else if (n.name().equals("title")) insideTitleNode = false;
}
}
}
Zasadniczo przetwarzanie XML szybko zmienia się w ogromną pętlę sterowaną maszyną stanu, z dużą ilością zmiennych stanu używanych do wskazywania węzłów nadrzędnych, które znaleźliśmy wcześniej. W przeciwnym razie należy zachować obiekt stosu, aby śledzić wszystkie zagnieżdżone znaczniki. Szybko staje się to podatne na błędy i trudne do utrzymania.
Ponownie wydaje się, że problemem jest to, że dane, którymi jesteśmy zainteresowani, nie są bezpośrednio powiązane z pojedynczym węzłem. Jasne, może tak być, gdybyśmy napisali XML w następujący sposób:
<book title="Blah blah" author="blah blah" price="15 USD" />
... ale tak rzadko używa się XML w rzeczywistości. Przeważnie mamy węzły tekstowe jako dzieci węzłów nadrzędnych i musimy śledzić węzły nadrzędne, aby ustalić, do czego odnosi się węzeł tekstowy.
Więc ... robię coś złego? Czy jest lepszy sposób? W którym momencie użycie parsera opartego na strumieniu XML staje się zbyt skomplikowane, więc niezbędny jest w pełni parser DOM? Chciałbym usłyszeć od innych programistów, jakiego rodzaju idiomów używają podczas przetwarzania XML z analizatorami strumieniowymi. Czy parsowanie strumieniowe XML musi zawsze zamieniać się w wielką maszynę stanów?
źródło
Odpowiedzi:
Dla mnie pytanie jest odwrotne. W którym momencie dokument XML staje się tak niewygodny, że musisz zacząć używać SAX zamiast DOM?
Używałbym SAX tylko dla bardzo dużego strumienia danych o nieokreślonej wielkości; lub jeśli zachowanie, które ma wywoływać XML, jest w rzeczywistości oparte na zdarzeniach, a zatem podobne do SAX.
Podany przez Ciebie przykład wygląda dla mnie bardzo DOM.
EDYCJA: Używałbym również SAX dla strumieni, które mogą być zniekształcone, ale tam, gdzie chcę, najlepiej zgadnąć, jak wyciągnąć dane.
źródło
Nie za bardzo pracuję z XML, moim zdaniem trochę, prawdopodobnie jednym z najlepszych sposobów parsowania XML za pomocą biblioteki jest używanie XPath.
Zamiast przeszukiwać drzewo w celu znalezienia określonego węzła, podajesz ścieżkę do niego. W przypadku twojego przykładu (w pseudokodzie) byłoby to coś takiego:
XPath jest znacznie potężniejszy, możesz wyszukiwać przy użyciu warunków (zarówno wartości, jak i atrybutów), wybierać określony węzeł na liście, przenosić poziomy przez drzewo. Polecam poszukać informacji o tym, jak go używać, jest zaimplementowany w wielu bibliotekach parsujących (używam go w wersji .Net Framework i lxml dla Pythona)
źródło
Zwykle tak jest.
Dla mnie wskazanie użycia pełnoprawnego analizatora składni DOM jest wtedy, gdy musiałbym naśladować niektóre części hierarchii plików w pamięci, na przykład, aby móc rozpoznać odsyłacze w dokumencie.
źródło
Ogólnie parsowanie jest po prostu sterowaniem maszyną stanu, a parsowanie XML nie jest niczym innym. Parsowanie strumieniowe jest zawsze kłopotliwe, zawsze kończę budowanie jakiegoś stosu, aby śledzić węzły przodków, i definiowanie wielu zdarzeń oraz jakiegoś programu rozsyłającego zdarzenia, który sprawdza rejestr znaczników lub ścieżek i odpala zdarzenie jeśli jeden pasuje. Kod podstawowy jest dość ciasny, ale kończę z ogromną ilością procedur obsługi zdarzeń, które polegają głównie na przypisaniu wartości następnego węzła tekstowego do pola w jakiejś strukturze. Może stać się dość włochaty, jeśli trzeba tam również mieszać logikę biznesową.
Zawsze używałbym DOM, chyba że problemy z rozmiarem lub wydajnością podyktowałyby inaczej.
źródło
Nie do końca agnostyk językowy, ale zwykle deserializuję XML na obiekty, a nawet nie myślę o analizie. Jedyny czas, aby martwić się samemu analizowaniem strategii, to problem z prędkością.
źródło
Stanie się znacznie mniej uciążliwe, jeśli można używać XPath. A w .Net land LINQ to XML również streszcza wiele mniej efektownych rzeczy. ( Edycja - wymagają oczywiście podejścia DOM)
Zasadniczo, jeśli używasz podejścia opartego na strumieniu (więc nie możesz używać ładniejszych abstrakcji, które wymagają DOM), myślę, że zawsze będzie to dość kłopotliwe i nie jestem pewien, czy jest na to jakiś sposób.
źródło
Jeśli potrafisz znaleźć parser, który daje ci iterator, czy myślałeś o potraktowaniu go jak leksykon i użyciu generatora maszyny stanów?
źródło