Tecniche per l'analisi di XML

11

Ho sempre trovato XML piuttosto complicato da elaborare. Non sto parlando di implementare un parser XML: sto parlando di usando un parser esistente basato sul flusso, 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 l'XML, trovo che sia un po 'contorto. Il problema essenziale sembra essere che un documento XML è logicamente separato in singoli nodi, eppure i dati tipi e gli attributi sono spesso separati dai dati reali, a volte da più livelli di nidificazione. Pertanto, quando si elabora individualmente un particolare nodo, è necessario mantenere un sacco di stato aggiuntivo 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 dovrei determinare quando ho incontrato un nodo di testo che contiene un titolo di un libro? Supponiamo di avere un semplice parser XML che agisce come un iteratore, dandoci il prossimo nodo 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;
        }
    }
}

In sostanza, l'elaborazione XML si trasforma rapidamente in un enorme ciclo guidato dallo stato, con molte variabili di stato utilizzate per indicare i nodi padre che abbiamo trovato in precedenza. In caso contrario, è 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 sono direttamente associati a un singolo nodo. Certo, potrebbe essere, se scrivessimo l'XML come:

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

... ma raramente viene usato il XML nella realtà. Principalmente abbiamo nodi di testo come figli di nodi parent e abbiamo bisogno di tenere traccia dei nodi genitore 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 XML basato sul flusso diventa troppo macchinoso, per cui diventa necessario un parser DOM completo? Mi piacerebbe sapere da altri programmatori che tipo di idiomi usano durante l'elaborazione di XML con parser basati sul flusso. L'analisi XML basata sul flusso deve sempre trasformarsi in un'enorme macchina a stati?

    
posta Channel72 08.04.2011 - 16:24
fonte

7 risposte

9

Per me, la domanda è il contrario. A che punto un documento XML diventa così ingombrante, che devi iniziare a usare SAX invece di DOM?

Userei solo SAX per un flusso di dati molto ampio e di dimensioni indeterminate; o se il comportamento che l'XML intende invocare è realmente basato sugli eventi, e quindi simile a SAX.

L'esempio che offri è molto simile a DOM.

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

EDIT: Userei anche SAX per flussi che potrebbero essere malformati, ma dove voglio fare una buona ipotesi per ottenere i dati.

    
risposta data 08.04.2011 - 16:38
fonte
7

Non lavoro troppo con XML, un po 'a mio parere, probabilmente uno dei migliori modi di analizzare l'XML con una libreria sta usando XPath.

Invece di attraversare l'albero per trovare un nodo specifico, devi dare un percorso. Nel caso del tuo esempio (in pseudocodice), sarebbe qualcosa di simile a:

books = parent.xpath("/book") // This would give you all the book nodes
for-each book in books
    title = book.xpath("/title/text()")
    author = book.xpath("/author/text()")
    price = book.xpath("/price/text()")

    // Do things with the data

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

    
risposta data 08.04.2011 - 16:43
fonte
5

Must stream-based XML parsing always turn into a huge state machine?

Di solito sì, sì.

Per me scegliere di usare 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.

    
risposta data 08.04.2011 - 16:29
fonte
4

L'analisi in generale sta semplicemente guidando una macchina a stati e l'analisi XML non è diversa. L'analisi basata sul flusso è sempre una seccatura, finisco sempre per creare uno stack di qualche tipo per tenere traccia dei nodi antenati e definire molti eventi e qualche tipo di dispatcher di eventi che controlla un registro di tag o di traccia e attiva un evento se uno corrisponde. Il codice core è abbastanza stretto, ma finisco con un enorme wad 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 la logica di business anche lì.

Utilizzerei sempre il DOM a meno che le dimensioni oi problemi di prestazione non fossero dettati diversamente.

    
risposta data 08.04.2011 - 17:39
fonte
1

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

    
risposta data 08.04.2011 - 16:41
fonte
0

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

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.

    
risposta data 08.04.2011 - 16:46
fonte
0

Se riesci a trovare un parser che ti dà un iteratore, hai pensato di trattarlo come un lexer e utilizzare un generatore di macchine di stato?

    
risposta data 23.02.2017 - 15:06
fonte

Leggi altre domande sui tag