Techniques d'analyse syntaxique XML


11

J'ai toujours trouvé XML un peu lourd à traiter. Je ne parle pas d'implémenter un analyseur XML: je parle d' utiliser un analyseur basé sur un flux existant, comme un analyseur SAX, qui traite le nœud XML nœud par nœud.

Oui, il est vraiment facile d'apprendre les différentes API de ces analyseurs, mais chaque fois que je regarde du code qui traite XML, je le trouve toujours quelque peu compliqué. Le problème essentiel semble être qu'un document XML est logiquement séparé en nœuds individuels, et pourtant les types de données et les attributs sont souvent séparés des données réelles, parfois par plusieurs niveaux d'imbrication. Par conséquent, lors du traitement individuel d'un nœud particulier, de nombreux états supplémentaires doivent être maintenus pour déterminer où nous en sommes et ce que nous devons faire ensuite.

Par exemple, étant donné un extrait d'un document XML typique:

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

... Comment pourrais-je déterminer quand j'ai rencontré un nœud de texte contenant un titre de livre? Supposons que nous ayons un analyseur XML simple qui agit comme un itérateur, nous donnant le nœud suivant dans le document XML chaque fois que nous appelons XMLParser.getNextNode(). Je me retrouve inévitablement à écrire du code comme ceci:

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

Fondamentalement, le traitement XML se transforme rapidement en une énorme boucle pilotée par une machine d'état, avec de nombreuses variables d'état utilisées pour indiquer les nœuds parents que nous avons trouvés plus tôt. Sinon, un objet de pile doit être conservé pour garder une trace de toutes les balises imbriquées. Cela devient rapidement sujet aux erreurs et difficile à maintenir.

Encore une fois, le problème semble être que les données qui nous intéressent ne sont pas directement associées à un nœud individuel. Bien sûr, cela pourrait être le cas si nous écrivions le XML comme:

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

... mais c'est rarement ainsi que XML est utilisé dans la réalité. Généralement, nous avons des nœuds de texte en tant qu'enfants des nœuds parents, et nous devons garder une trace des nœuds parents afin de déterminer à quoi un nœud de texte fait référence.

Alors ... je fais quelque chose de mal? Y a-t-il une meilleure façon? À quel moment l'utilisation d'un analyseur basé sur un flux XML devient-elle trop lourde, de sorte qu'un analyseur DOM à part entière devient nécessaire? J'aimerais entendre d'autres programmeurs sur le type d'idiomes qu'ils utilisent lors du traitement de XML avec des analyseurs basés sur des flux. L'analyse XML basée sur les flux doit-elle toujours devenir une énorme machine à états?


2
si vous utilisez un langage .net, vous devriez regarder linq to xml aka XLinq.
Muad'Dib

Merci, je pensais que j'étais le seul à avoir ce problème. Franchement, je trouve souvent que tout le format XML est plus un obstacle qu'une aide. Oui, cela permet de stocker un grand nombre de données structurées dans un petit fichier texte. Mais si vous avez besoin de plus de 20 classes pour déballer et donner un sens à la chose - sans aucune garantie que vous ne négligez pas quelque chose de plus ou moins important. C'est comme le lapin du Saint Graal de Monty Python.
Elise van Looij

Réponses:


9

Pour moi, la question est dans l'autre sens. À quel moment un document XML devient-il si lourd, que vous devez commencer à utiliser SAX au lieu de DOM?

Je n'utiliserais SAX que pour un très grand flux de données de taille indéterminée; ou si le comportement que le XML est censé invoquer est vraiment piloté par les événements, et donc semblable à SAX.

L'exemple que vous donnez me ressemble beaucoup à DOM.

  1. Charger le XML
  2. Extrayez le ou les nœuds de titre et "faites quelque chose avec eux".

EDIT: J'utiliserais également SAX pour les flux qui pourraient être mal formés, mais où je veux faire une meilleure supposition pour extraire les données.


2
Je pense que c'est un bon point. Si vous analysez des documents trop volumineux pour DOM, vous devez déterminer si vous analysez des documents trop volumineux pour XML
Dean Harding

1
+1: Étant donné l'option, j'irais toujours avec DOM. Malheureusement, il semble que nos exigences de conception incluent toujours «la capacité de gérer n'importe quel document» et «doivent être performantes», ce qui exclut à peu près les solutions basées sur DOM.
TMN

3
@TMN, dans un monde idéal où les exigences excluraient XML en premier lieu.
SK-logic

1
@TMN, cela ressemble à l'une de ces exigences fantômes: "Bien sûr, tous nos documents ne font que 100 Ko environ, et le plus gros que nous ayons vu est 1 Mo, mais vous ne savez jamais ce que l'avenir nous réserve, nous devons donc garder nos options ouvertes et construire pour des documents infiniment gros "
Paul Butcher

@Paul Butcher, on ne sait jamais. Je veux dire, un vidage de Wikipedia est comme 30 Go de XML.
Channel72

7

Je ne travaille pas trop avec XML, à mon avis, probablement l'un des meilleurs moyens d'analyser XML avec une bibliothèque est d'utiliser XPath.

Au lieu de parcourir l'arborescence pour trouver un nœud spécifique, vous lui donnez un chemin. Dans le cas de votre exemple (en pseudocode), ce serait quelque chose comme:

books = parent.xpath ("/ book") // Cela vous donnerait tous les nœuds du livre
pour chaque livre dans les livres
    title = book.xpath ("/ title / text ()")
    author = book.xpath ("/ author / text ()")
    price = book.xpath ("/ price / text ()")

    // Faites des choses avec les données

XPath est beaucoup plus puissant que cela, vous pouvez rechercher en utilisant des conditions (à la fois sur des valeurs et des attributs), sélectionner un nœud spécifique dans une liste, déplacer des niveaux dans l'arborescence. Je vous recommande de chercher des informations sur la façon de l'utiliser, il est implémenté dans de nombreuses bibliothèques d'analyse (je l'utilise la version .Net Framework et lxml pour Python)


C'est bien si vous pouvez connaître et faire confiance à l'avance à la manière dont le XML est structuré. Si vous ne savez pas si, disons, la largeur d'un élément sera spécifiée en tant qu'attribut d'un nœud ou en tant que nœud d'attribut à l'intérieur du nœud de taille d'un élément, alors XPath ne sera pas d'une grande aide.
Elise van Looij

5

L'analyse XML basée sur les flux doit-elle toujours devenir une énorme machine à états?

Habituellement, oui.

Pour moi, pointer pour utiliser un analyseur DOM à part entière, c'est quand j'aurais besoin d'imiter des parties de la hiérarchie des fichiers en mémoire, par exemple pour pouvoir résoudre les références croisées dans le document.


+1: Commencez avec DOM. Évitez SAX.
S.Lott

ou avec vtd-xml
vtd-xml-author

4

L'analyse en général conduit simplement une machine à états, et l'analyse XML n'est pas différente. L'analyse basée sur les flux est toujours un problème, je finis toujours par créer une pile quelconque pour garder une trace des nœuds ancêtres, et définir de nombreux événements et une sorte de répartiteur d'événements qui vérifie un registre de balises ou de chemins et déclenche un événement si on correspond. Le code de base est assez serré, mais je me retrouve avec une énorme liasse de gestionnaires d'événements qui consistent principalement à affecter la valeur du nœud de texte suivant à un champ dans une structure quelque part. Cela peut devenir assez poilu si vous devez également y mêler la logique métier.

J'utiliserais toujours DOM à moins que des problèmes de taille ou de performance ne dictent le contraire.


1

Pas complètement indépendant du langage, mais je désérialise généralement le XML en objets plutôt que même de penser à l'analyse. Seul le temps de vous soucier des stratégies d'analyse en soi est si vous avez un problème de vitesse.


Cela relève de l'analyse. Sauf si le XML en question est la sortie de la sérialisation d'objet et que vous avez une bibliothèque de désérialisation prête à l'emploi. Mais alors cette question n'apparaît pas.

De nombreuses langues / piles ont des bibliothèques de désérialisation prêtes à l'emploi.
Wyatt Barnett

Ouais, alors quoi? Mes points sont toujours valables - tous les fichiers XML à l'état sauvage ne sont pas dans un tel format, et si vous en avez un, vous ne posez pas cette question car vous utilisez simplement cette bibliothèque de désérialisation et n'analysez rien par vous-même, à partir de flux ou autrement.

0

Cela devient beaucoup moins lourd si vous pouvez utiliser XPath. Et dans .Net land, LINQ to XML résume également beaucoup de choses moins glamour. ( Modifier - cela nécessite bien sûr une approche DOM)

Fondamentalement, si vous adoptez une approche basée sur les flux (donc vous ne pouvez pas utiliser de meilleures abstractions qui nécessitent un DOM), je pense que ce sera toujours assez lourd et je ne suis pas sûr qu'il existe un moyen de contourner cela.


Si vous utilisez XPath, vous utilisez DOM (sauf si vous l'utilisez avec un évaluateur XPath local).
TMN

oui, d'où mon commentaire sur les abstractions nécessitant DOM ... mais je vais clarifier, merci!
Steve

0

Si vous pouvez trouver un analyseur qui vous donne un itérateur, avez-vous pensé à le traiter comme un lexer et à utiliser un générateur de machine d'état?

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.