Tecniche per l'analisi di XML


11

Ho sempre trovato XML un po 'complicato da elaborare. Non sto parlando dell'implementazione di un parser XML: sto parlando dell'utilizzo di un parser esistente basato su stream, come un parser SAX, che elabora il nodo XML per nodo.

Sì, è davvero facile imparare le varie API per questi parser, ma ogni volta che guardo il codice che elabora XML trovo sempre che sia un po 'contorto. Il problema essenziale sembra essere che un documento XML sia logicamente separato in singoli nodi, eppure i tipi di dati e gli attributi sono spesso separati dai dati reali, a volte da più livelli di annidamento. Pertanto, quando si elabora un determinato nodo singolarmente, è necessario mantenere un sacco di stato extra per determinare dove siamo e cosa dobbiamo fare dopo.

Ad esempio, dato uno snippet da un tipico documento XML:

<book>
  <title>Blah blah</title>
  <author>Blah blah</author>
  <price>15 USD</price>
</book>

... Come determinerei quando ho incontrato un nodo di testo contenente un titolo di libro? Supponiamo di avere un semplice parser XML che agisce come un iteratore, dandoci il nodo successivo nel documento XML ogni volta che chiamiamo XMLParser.getNextNode(). Mi ritrovo inevitabilmente a scrivere codice come il seguente:

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;
        }
    }
}

Fondamentalmente, l'elaborazione XML si trasforma rapidamente in un enorme ciclo guidato da una macchina a stati, con molte variabili di stato utilizzate per indicare nodi padre che abbiamo trovato in precedenza. Altrimenti, è necessario mantenere un oggetto stack per tenere traccia di tutti i tag nidificati. Questo diventa rapidamente soggetto a errori e difficile da mantenere.

Ancora una volta, il problema sembra essere che i dati a cui siamo interessati non siano direttamente associati a un singolo nodo. Certo, potrebbe essere, se abbiamo scritto l'XML come:

<book title="Blah blah" author="blah blah" price="15 USD" />

... ma questo è raramente il modo in cui XML viene utilizzato nella realtà. Principalmente abbiamo nodi di testo come figli di nodi principali e dobbiamo tenere traccia dei nodi principali per determinare a cosa si riferisce un nodo di testo.

Quindi ... sto facendo qualcosa di sbagliato? Esiste un modo migliore? A che punto l'utilizzo di un parser basato su stream XML diventa troppo complicato, quindi diventa necessario un parser DOM completo? Mi piacerebbe sentire da altri programmatori che tipo di idiomi usano durante l'elaborazione di XML con parser basati su stream. L'analisi XML basata su stream deve sempre trasformarsi in un'enorme macchina a stati?


2
se stai usando un linguaggio .net, dovresti guardare linq to xml aka XLinq.
Muad'Dib,

Grazie, pensavo di essere l'unico con questo problema. Francamente, trovo spesso che l'intero formato XML sia più un ostacolo che un aiuto. Sì, consente di archiviare molti dati strutturati in un piccolo file di testo. Ma se hai bisogno di oltre 20 classi per decomprimere e dare un senso alla cosa, senza alcuna garanzia che non stai trascurando qualcosa di più o meno importante. È come il coniglio nel Sacro Graal di Monty Python.
Elise van Looij,

Risposte:


9

Per me, la domanda è al contrario. A che punto un documento XML diventa così ingombrante, che devi iniziare a utilizzare SAX anziché DOM?

Vorrei usare SAX solo per un flusso di dati molto grande, di dimensioni indeterminate; o se il comportamento che l'XML intende invocare è realmente guidato dagli eventi e quindi simile a SAX.

L'esempio che mi dai sembra molto simile a DOM.

  1. Carica l'XML
  2. Estrai i nodi del titolo e "fai qualcosa con loro".

EDIT: Utilizzerei anche SAX per stream che potrebbero essere malformati, ma dove voglio fare un'ipotesi migliore per ottenere i dati.


2
Penso che questo sia un buon punto. Se stai analizzando documenti troppo grandi per DOM, devi considerare se stai analizzando documenti troppo grandi per XML
Dean Harding

1
+1: data l'opzione, andrei sempre con DOM. Sfortunatamente, sembra che i nostri requisiti di progettazione includano sempre "capacità di gestire documenti di qualsiasi dimensione" e "devono essere performanti", che praticamente escludono soluzioni basate su DOM.
TMN,

3
@TMN, in un mondo ideale che i requisiti escluderebbero in primo luogo l'XML.
SK-logic,

1
@TMN, sembra uno di quei requisiti fantasma: "Naturalmente tutti i nostri documenti sono solo circa 100 KB, e il più grande che abbiamo visto è 1 MB, ma non sai mai cosa riserva il futuro, quindi dovremmo tenere aperte le nostre opzioni e costruire per documenti infinitamente grandi "
Paul Butcher,

@ Paul Butcher, non lo sai mai. Voglio dire, un dump di Wikipedia è come 30 GB di XML.
Channel72,

7

Non lavoro troppo con XML, bit secondo me, probabilmente uno dei modi migliori per analizzare XML con una libreria è usare XPath.

Invece di attraversare l'albero per trovare un nodo specifico, si fornisce un percorso ad esso. Nel caso del tuo esempio (in pseudocodice), sarebbe qualcosa del tipo:

books = parent.xpath ("/ book") // Questo ti darebbe tutti i nodi del libro
per ogni libro nei libri
    title = book.xpath ("/ title / text ()")
    author = book.xpath ("/ author / text ()")
    price = book.xpath ("/ price / text ()")

    // Fai le cose con i dati

XPath è molto più potente di così, puoi cercare usando condizioni (sia su valori che su attributi), selezionare un nodo specifico in un elenco, spostare i livelli attraverso l'albero. Ti consiglio di cercare informazioni su come usarlo, è implementato in molte librerie di analisi (lo uso la versione .Net Framework e lxml per Python)


Va bene se puoi conoscere e fidarti in anticipo del modo in cui l'xml è strutturato. Se non sai se, per esempio, la larghezza di un elemento verrà specificata come un attributo di un nodo o come un nodo di attributo all'interno del nodo dimensione di un elemento, allora XPath non sarà di grande aiuto.
Elise van Looij,

5

L'analisi XML basata su stream deve sempre trasformarsi in un'enorme macchina a stati?

Di solito lo fa, sì.

Per me scegliere di utilizzare un parser DOM completo è quando avrei bisogno di imitare parti della gerarchia di file in memoria, ad esempio per essere in grado di risolvere i riferimenti incrociati all'interno del documento.


+1: inizia con DOM. Evitare SAX.
S.Lott

o con vtd-xml
vtd-xml-author il

4

L'analisi in generale sta semplicemente guidando una macchina a stati e l'analisi XML non è diversa. L'analisi basata sullo streaming è sempre una seccatura, finisco sempre per creare uno stack di qualche tipo per tenere traccia dei nodi antenati e definire molti eventi e un qualche tipo di dispatcher di eventi che controlla un registro di tag o di percorso e genera un evento se uno corrisponde. Il codice di base è abbastanza stretto, ma finisco con un'enorme mazzetta di gestori di eventi che consistono principalmente nell'assegnare il valore del seguente nodo di testo a un campo in una struttura da qualche parte. Può diventare piuttosto peloso se hai bisogno di mescolare anche la logica aziendale.

Utilizzerei sempre DOM a meno che diversamente indicato per problemi di dimensioni o prestazioni.


1

Non completamente indipendente dal linguaggio, ma in genere deserializzo l'XML in oggetti piuttosto che pensare all'analisi. L'unico momento per preoccuparsi di analizzare le strategie di per sé è se hai un problema di velocità.


Questo rientra nell'analisi. A meno che l'XML in questione non sia l'output della serializzazione degli oggetti e si disponga di una libreria di deserializzazione già pronta. Ma allora questa domanda non appare.

Molte lingue / stack hanno librerie di deserializzazione già pronte.
Wyatt Barnett,

Sì, e allora? I miei punti sono ancora validi: non tutti i file XML in natura hanno un formato simile e, se ne hai uno che lo fa, non poni questa domanda mentre usi semplicemente quella libreria di deserializzazione e non analizzi nulla da solo, da flussi o altro.

0

Diventa molto meno ingombrante se puoi usare XPath. E in .Net land LINQ to XML estrae anche molte cose meno glamour. ( Modifica - questi richiedono ovviamente un approccio DOM)

Fondamentalmente, se stai adottando un approccio basato sul flusso (quindi non puoi usare astrazioni più belle che richiedono un DOM) penso che sarà sempre piuttosto ingombrante e non sono sicuro che ci sia un modo per aggirare questo.


Se stai usando XPath, stai usando DOM (a meno che tu non lo stia usando con un valutatore XPath cresciuto in casa).
TMN,

sì, quindi il mio commento sulle astrazioni che richiedono DOM ... ma chiarirò, grazie!
Steve

0

Se riesci a trovare un parser che ti offre un iteratore, hai mai pensato di trattarlo come un lexer e di utilizzare un generatore di macchine a stati?

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.