Técnicas para analizar XML

11

Siempre he encontrado que XML es algo engorroso de procesar. No estoy hablando de implementar un analizador XML: estoy hablando de usar un analizador basado en flujo existente, como un analizador SAX, que procesa el nodo XML por nodo.

Sí, es realmente fácil aprender las diversas API para estos analizadores, pero cada vez que miro el código que procesa XML siempre encuentro que es algo complicado. El problema esencial parece ser que un documento XML está separado lógicamente en nodos individuales y, sin embargo, los tipos y atributos de datos a menudo están separados de los datos reales, a veces por múltiples niveles de anidamiento. Por lo tanto, cuando se procesa un nodo en particular individualmente, se debe mantener una gran cantidad de estado adicional para determinar dónde estamos y qué debemos hacer a continuación.

Por ejemplo, dado un fragmento de un documento XML típico:

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

... ¿Cómo determinaría cuándo me encuentro con un nodo de texto que contiene el título de un libro? Supongamos que tenemos un analizador XML simple que actúa como un iterador, dándonos el siguiente nodo en el documento XML cada vez que llamamos XMLParser.getNextNode(). Inevitablemente me encuentro escribiendo código como el siguiente:

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

Básicamente, el procesamiento XML se convierte rápidamente en un gran bucle controlado por máquinas de estado, con muchas variables de estado utilizadas para indicar nodos principales que hemos encontrado anteriormente. De lo contrario, se debe mantener un objeto de pila para realizar un seguimiento de todas las etiquetas anidadas. Esto rápidamente se vuelve propenso a errores y difícil de mantener.

Nuevamente, el problema parece ser que los datos que nos interesan no están directamente asociados con un nodo individual. Claro, podría ser, si escribiéramos el XML como:

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

... pero así es como se usa XML en la realidad. La mayoría de las veces tenemos nodos de texto como hijos de nodos principales, y necesitamos hacer un seguimiento de los nodos principales para determinar a qué se refiere un nodo de texto.

Entonces ... ¿estoy haciendo algo mal? ¿Hay una mejor manera? ¿En qué punto el uso de un analizador basado en flujo XML se vuelve demasiado engorroso, por lo que se hace necesario un analizador DOM completo? Me gustaría saber de otros programadores qué tipo de modismos usan cuando procesan XML con analizadores basados ​​en secuencias. ¿El análisis XML basado en secuencias debe convertirse siempre en una gran máquina de estados?

Canal72
fuente
2
si está utilizando un lenguaje .net, debería mirar linq to xml, también conocido como XLinq.
Muad'Dib
Gracias, pensé que era el único con este problema. Francamente, a menudo encuentro que todo el formato XML es más un obstáculo que una ayuda. Sí, permite almacenar muchos datos estructurados en un pequeño archivo de texto. Pero si necesita más de 20 clases para desempacar y darle sentido a la cosa, sin garantía de que no esté pasando por alto algo más o menos importante. Es como el conejito en el Santo Grial de Monty Python.
Elise van Looij

Respuestas:

9

Para mí, la pregunta es al revés. ¿En qué punto un documento XML se vuelve tan engorroso que tiene que comenzar a usar SAX en lugar de DOM?

Solo usaría SAX para un flujo de datos muy grande y de tamaño indeterminado; o si el comportamiento que pretende invocar el XML está realmente basado en eventos y, por lo tanto, es similar a SAX.

El ejemplo que das me parece muy DOM.

  1. Cargue el XML
  2. Extraiga los nodos del título y "haga algo con ellos".

EDITAR: También usaría SAX para transmisiones que pueden estar malformadas, pero donde quiero hacer una mejor suposición para obtener los datos.

Carnicero paul
fuente
2
Creo que este es un buen punto. Si está analizando documentos que son demasiado grandes para DOM, entonces debe considerar si está analizando documentos que son demasiado grandes para XML
Dean Harding
1
+1: Dada la opción, siempre iría con DOM. Desafortunadamente, parece que nuestros requisitos de diseño siempre incluyen "la capacidad de manejar documentos de cualquier tamaño" y "deben ser eficaces", lo que prácticamente descarta las soluciones basadas en DOM.
TMN
3
@ TMN, en un mundo ideal cuyos requisitos descartarían XML en primer lugar.
SK-logic
1
@ TMN, eso suena como uno de esos requisitos fantasmas: "Por supuesto, todos nuestros documentos tienen solo unos 100 KB, y el más grande que hemos visto es 1 MB, pero nunca se sabe lo que depara el futuro, por lo que debemos mantener nuestras opciones abiertas y construir para documentos infinitamente grandes "
Paul Butcher
@ Paul Butcher, nunca se sabe. Quiero decir, un volcado de Wikipedia es como 30 GB de XML.
Canal72
7

No trabajo demasiado con XML, aunque en mi opinión, probablemente una de las mejores formas de analizar XML con una biblioteca es usando XPath.

En lugar de atravesar el árbol para encontrar algún nodo específico, le da una ruta. En el caso de su ejemplo (en pseudocódigo), sería algo así como:

books = parent.xpath ("/ book") // Esto te daría todos los nodos del libro
para cada libro en libros
    title = book.xpath ("/ title / text ()")
    author = book.xpath ("/ author / text ()")
    precio = book.xpath ("/ precio / texto ()")

    // Hacer cosas con los datos

XPath es mucho más poderoso que eso, puede buscar usando condiciones (tanto en valores como en atributos), seleccionar un nodo específico en una lista, mover niveles a través del árbol. Le recomiendo que busque información sobre cómo usarlo, está implementado en muchas bibliotecas de análisis (lo uso la versión .Net Framework y lxml para Python)

Ioachim
fuente
Está bien si puede conocer y confiar de antemano en la forma en que se estructura el XML. Si no sabe si, por ejemplo, el ancho de un elemento se especificará como un atributo de un nodo o como un nodo de atributo dentro del nodo de tamaño de un elemento, entonces XPath no será de mucha ayuda.
Elise van Looij
5

¿El análisis XML basado en secuencias debe convertirse siempre en una gran máquina de estados?

Por lo general, sí.

Para mí, utilizar un analizador DOM completo es cuando necesito imitar partes de la jerarquía de archivos en memoria, por ejemplo, para poder resolver referencias cruzadas dentro del documento.

Alexander Gessler
fuente
+1: Comience con DOM. Evitar el saxo.
S.Lott
o con vtd-xml
vtd-xml-author
4

Analizar en general es simplemente manejar una máquina de estado, y el análisis XML no es diferente. El análisis basado en secuencias siempre es una molestia, siempre termino construyendo una pila de algún tipo para realizar un seguimiento de los nodos ancestrales y definiendo muchos eventos y algún tipo de despachador de eventos que verifica un registro de etiqueta o ruta y dispara un evento si uno coincide El código central es bastante estricto, pero termino con una gran cantidad de controladores de eventos que consisten principalmente en asignar el valor del siguiente nodo de texto a un campo en una estructura en algún lugar. Puede ser bastante complicado si necesita mezclar la lógica de negocios allí también.

Siempre usaría DOM a menos que los problemas de tamaño o rendimiento dictaran lo contrario.

TMN
fuente
1

No es completamente independiente del lenguaje, pero normalmente deserializo el XML en objetos en lugar de pensar en el análisis. El único momento para preocuparse por las estrategias de análisis per se es si tiene un problema de velocidad.

Wyatt Barnett
fuente
Eso cae bajo análisis. A menos que el XML en cuestión sea el resultado de la serialización de objetos y tenga una biblioteca de deserialización lista para usar. Pero entonces esta pregunta no aparece.
Muchos idiomas / pilas tienen bibliotecas de deserialización integradas.
Wyatt Barnett
Si, y que? Mis puntos siguen siendo válidos: no todos los archivos XML en la naturaleza tienen ese formato, y si tiene uno que sí lo hace, no haga esta pregunta, ya que solo usa esa biblioteca de deserialización y no analiza nada por su cuenta, de corrientes o de otra manera.
0

Se vuelve mucho menos engorroso si puede usar XPath. Y en .Net land, LINQ to XML también extrae muchas de las cosas menos glamorosas. ( Editar : estos requieren un enfoque DOM, por supuesto)

Fundamentalmente, si está adoptando un enfoque basado en la transmisión (por lo que no puede utilizar abstracciones más agradables que requieren un DOM), creo que siempre será bastante engorroso y no estoy seguro de que haya alguna forma de evitarlo.

Steve
fuente
Si está utilizando XPath, está utilizando DOM (a menos que lo esté utilizando con un evaluador XPath de cosecha propia).
TMN
sí, de ahí mi comentario sobre las abstracciones que requieren DOM ... pero lo aclararé, ¡gracias!
Steve
0

Si puede encontrar un analizador que le proporcione un iterador, ¿ha pensado en tratarlo como un lexer y utilizar un generador de máquina de estados?

Demi
fuente