XmlStruct : Extraire et comparer la structure de documents xml

www.vbfrance.com/code.aspx?ID=47888

Documentation : XmlStruct.html

Code source : XmlStruct.vbproj.html

Par Patrice Dargenton : patrice.dargenton@free.fr

http://patrice.dargenton.free.fr/CodesSources/index.html

13/09/2008

Ca fait un bout de temps que je cherche à faire une version française du célèbre Robot de discussion Alice. Il existe certes des jeux AIML en français, mais je n'ai jamais réussi à les faire fonctionner. Or depuis Septembre 2007, il existe une version DotNet d'Alice avec les sources en C#, j'ai converti le code en VB ici :

http://patrice.dargenton.free.fr/ia/alice/alicechatbot.html

Une nouvelle tentative est donc possible, et pour la première fois avec les sources complètes en VB, ce qui devrait permettre d'aller plus loin dans le diagnostique de la version française. L'idée de base est d'analyser la structure des documents aiml qui fonctionne effectivement en anglais, avec l'objectif de vérifier et d'appliquer, le cas échéant, la structure correcte aux fichiers aiml en français. Il se trouve qu'il existe un schéma xsd officiel pour valider les fichiers aiml, mais en pratique, aucun des jeux testés ne respectent complètement le schéma officiel ! (voir la doc. d'AliceVB).

Une solution plus pragmatique consiste donc à déduire le schéma selon le dernier jeu AIML qui fonctionne. Pour cela, il faut fusionner l'ensemble des fichiers aiml afin d'avoir la liste complète des formats rencontré (un simple copy /b *.aiml Cumul.aiml suffit, à condition de retirer la balise d'entête et de fin de chaque fichier, cela fonctionne si l'encodage des fichiers est identique).

En répétant la manipulation avec les fichiers en français, on obtient cette fois le schéma utilisé en français. Cependant, la comparaison des schémas Anglais et Français est à peu près illisible via l'utilitaire WinDiff. D'où l'idée de comparer plutôt via le DTD (Document Type Definition), lequel est un schéma simplifié beaucoup plus lisible. Le seul problème est que le DTD ne permet pas de gérer l'aspect récursif (hiérarchique) des fichiers aiml. Cependant, on peut contourner cette limite en renommant avec un numéro les balises susceptibles d'apparaître à plusieurs niveaux, de façon à les dissocier et à faire disparaître complètement l'aspect récursif de la structure AIML. Les fichiers AIML ainsi mis à plat ne fonctionneront plus avec le robot de discussion, mais on pourra alors extraire la DTD et vérifier, d'une part que les fichiers transformés respectent bien leur DTD (pour voir si l'extraction DTD est juste), et ensuite comparer leur DTD plus facilement qu'avec leur schéma xsd. Je n'ai pas trouvé de logiciel pour extraire le DTD (sauf peut être TRANG), alors j'en ai conçu un, mais en ignorant les attributs dans cette première version.

Voici donc un ensemble d'utilitaires pour comparer la structure de fichiers xml via WinDiff.

 

Table des matières

Documentation. 2

Mode d'emploi 2

Correction des fichiers xml 2

Corriger la présentation Xml 2

Normaliser la casse des balises Xml 2

Supprimer les attributs Xml 2

Extraction de la structure. 2

Extraire la structure au format DTD.. 2

Extraire la structure simple au format DTD.. 3

Vérification de la structure. 3

Vérifier selon la DTD.. 3

Vérifier selon le schéma. 4

Technique pour comparer des documents xml via leur DTD.. 4

Exemple de mise à plat xml 4

Limitations. 6

Historique des versions. 6

Version 1.01 du 07/09/2008 : Première version. 6

Liens. 6

 

 

Documentation

 

Mode d'emploi

Il suffit d'installer les menus contextuels : pour cela lancer simplement XmlStruct, puis cliquez sur OK pour "Ajouter les menus contextuels".

Ensuite il suffit d'utiliser les menus qui apparaissent lorsque l'on sélectionne un fichier Xml avec le bouton droit de la souris dans l'explorateur de fichiers de Windows. J'ai ajouté aussi la prise en compte des fichiers aiml comme des fichiers xml, il est possible d'ajouter facilement dans le code d'autres types de document xml avec une extension différente de xml.

Pour désinstaller (ou réinstaller XmlStruct en cas de déplacement), relancer simplement XmlStruct et cliquez sur OK pour "Enlever les menus contextuels.".

 

Mode multi-fichier : les messages ne sont affichés qu'une fois (au maximum, et parfois pas du tout), ce qui permet de lancer autant d'instance de XmlStruct qu'il y a de fichier à traiter. Par exemple, vous pouvez vérifier la validité de structure de l'ensemble des fichiers sélectionnés : un fichier erreur sera généré pour chaque fichier contenant des erreurs, et pas pour les fichiers respectant leur schéma DTD ou xsd.

 

Pour comparer le contenu de deux fichiers ou répertoire, voir l'utilitaire WinDiff (gratuit).

 

 

Correction des fichiers xml

 

Corriger la présentation Xml

Utiliser ce menu pour changer la présentation (indentation) des fichiers pour Windows, notamment en ce qui concerne les sauts de ligne, afin de les adapter à Windows (par exemple pour leur consultation via le bloc-notes).

Code menu : Indenter

Notes :

- Ce menu ne fonctionne pas pour certains encodages (un fichier .bak est donc généré à chaque fois) ;

- La correction de la présentation n'est pas très poussée, par exemple elle ne traite pas les balises à l'intérieur d'un texte, je n'ai pas réussi à faire mieux en testant les différentes options possibles dans le code.

 

Normaliser la casse des balises Xml

Utiliser ce menu pour normaliser la casse des balises en minuscules, afin de pouvoir vérifier et comparer la structure xml du fichier dans le cas où il existerait des variations dans la casse : Une version distincte du fichier avec le suffixe "_Norm" sera générée (même s'il n'y a pas de différence avec l'original, cependant la présentation pourra être corrigée aussi).

Code menu : Normaliser

 

Supprimer les attributs Xml

Utiliser ce menu pour retirer les attributs dans le fichier xml. Une version distincte du fichier avec le suffixe "_SansAttr" sera générée (même s'il n'y a pas de différence avec l'original, cependant la présentation pourra être corrigée aussi). Cette opération peut être longue, compter plusieurs minutes pour les fichiers de plusieurs Mo.

Code menu : SupprAttr

 

 

Extraction de la structure

 

Extraire la structure au format DTD

Utiliser ce menu pour extraire la structure au format DTD (les attributs sont ignorés dans le DTD). Si le fichier ne contient pas d'attribut, alors on peut maintenant le valider via ce DTD, en ajoutant par exemple la ligne :

<!DOCTYPE aiml SYSTEM 'aiml_deduit.dtd'>

Code menu : Xml2DTD

 

Notes :

- Il ne doit pas y avoir de commentaire avant la 1ère balise racine (car il n'y a pas de boucle sur le premier noeud) ;

- Ne pas indiquer de DTD avant de l'extraire (si on fait plusieurs tentatives), sinon une version sans DTD sera produite à l'occasion ;

- Les signes ?, + ou * peuvent être indiqués pour chaque élément enfant d'une liste :

? = 0 ou 1 élément

+ = Au moins 1 élément

* = 0 ou plusieurs éléments

Ces signes peuvent être appliqués à un élément enfant ou bien à une liste d'éléments enfants :

 

- Un et un seul élément :

    <!ELEMENT element-name (child-name)>

- Au moins 1 élément :

    <!ELEMENT element-name (child-name+)>

- 0 ou plus d'un élément :

    <!ELEMENT element-name (child-name*)>

- 0 ou 1 élément :

    <!ELEMENT element-name (child-name?)>

- Plusieurs types d'enfant (ou) :

    <!ELEMENT element-name (child1, child2, ...)>

- Contenu mixte : 0 ou plusieurs occurrences de child1 ou child2 :

    <!ELEMENT element-name (child1, child2)*>

- Imbrication :

    <!ELEMENT note (to, from, header, (message | body))>

- Mixage des signes (possible ?) :

    <!ELEMENT element-name (child1+, child2*)>

J'avais commencé à implémenter ce niveau de détail, mais je suis arrivé à une erreur de validation comme quoi on ne pouvait mélanger les signes + et * au sein d'une liste, du coup j'ai simplement indiqué le signe * pour l'ensemble des éléments de la liste, ce qui fonctionne toujours, mais est moins précis (on peut réactiver le code en commentaire, mais il faut trouver exactement ce qui ne fonctionne pas, en particulier voir si c'est une limite de la spécification ou bien de l'implémentation du validateur dans Visual Studio ou Internet Explorer).

 

Extraire la structure simple au format DTD

Utiliser ce menu pour extraire la structure au format DTD simple (les attributs sont ignorés dans le DTD), c'est-à-dire en évitant de numéroter les balises qui apparaissent de façon récursive (ou hiérarchique). Ce menu ne sert que pour la comparaison de structure, car le DTD ne permet plus de valider le document dans ce cas.

Code menu : Xml2DTDComp

 

 

Vérification de la structure

 

Vérifier selon la DTD

Le fichier doit référencer un DTD pour pouvoir être validé, il doit apparaître sur la seconde ligne après l'encodage, par exemple :

<!DOCTYPE aiml SYSTEM 'aiml_deduit.dtd'>

Code menu : VerifierDTD

 

Vérifier selon le schéma

Le fichier doit référencer un schéma xsd pour pouvoir être validé, il doit apparaître sur la seconde ligne après l'encodage, par exemple :

<aiml xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance

      xsi:noNamespaceSchemaLocation="aiml_deduit.xsd">

Code menu : VerifierSchema

 

Une fois le schéma précisé, l'éventuelle liste des erreurs produites (dans un fichier texte) est identique à celle produite par Visual Studio.

 

Notes :

- Pour extraire (déduire) un schéma depuis un fichier xml existant, voir Xml2Schema ;

- Le schéma xsd sur le web ne fonctionne pas toujours, on obtient parfois l'avertissement : "Le délai d'attente de l'opération a expiré". Une solution est de remplacer les références par une référence locale, en téléchargeant le schéma xsd au préalable, mais ce n'est pas toujours possible, par exemple s'il y a un préfixe html dans les espaces de nommage.

 

 

Technique pour comparer des documents xml via leur DTD

 

Voici la technique pour comparer des documents xml via leur DTD avec XmlStruct. Comme XmlStruct extrait le DTD en ignorant les attributs dans cette première version du logiciel, si on veut ensuite valider le document selon le DTD, il faut le tester sur une version du document sans attribut, voici donc la procédure complète avec un exemple :

 

1°) Prendre un fichier xml sans schéma ni dtd, par exemple Music.aiml

      (Tester au préalable si le fichier est un fichier xml valide, par exemple en l'ouvrant dans Visual Studio ; on peut vérifier aussi si le fichier est valide selon un schéma existant ou déduit, pour s'assurer que l'on va comparer les bonnes versions de fichier).

2°) Enlever les attributs (cela peut être long, plusieurs minutes pour les fichiers de plusieurs Mo) :

      Music.xml -> Music_SansAttr.xml

3°) Normaliser la casse des balises :

      Music_SansAttr.xml -> Music_SansAttr_Norm.xml

4°) Extraire la structure DTD : il indique qu'une version plate a été générée (cf. doublon <set>) :

      Music_SansAttr_Norm.xml -> Music_SansAttr_Norm_Dedoub.xml

5°) Extraire la structure DTD : Music_SansAttr_Norm_Dedoub.dtd

6°) Ajouter le DTD dans le document, en ligne n°2, juste après la ligne 1 : <?xml ... ?> :

      <!DOCTYPE aiml SYSTEM 'Music_SansAttr_Norm_Dedoub.dtd'>

7°) Vérifier le fichier selon son DTD : Music_SansAttr_Norm_Dedoub.xml : valide !

8°) Enlever le DTD et refaire le DTD sans dédoublonner cette fois les noeuds (DTD simple)

      afin de faciliter la comparaison de structure

 

 

Exemple de mise à plat xml

 

Fichier original (Music.aiml) :

 

<aiml>

<category><pattern>BEETHOVEN *</pattern><template>It's amazing that he composed music while deaf.

<think><set name="he"><set name="topic">BEETHOVEN</set></set></think>

</template></category>

<category><pattern>WHAT WAS THE * BEETHOVEN *</pattern><template>Fidelio.</template></category>

<category><pattern>WHO IS BEETHOVEN</pattern><template>The dog or the deaf composer?</template></category>

<category><pattern>WHO IS LUDWIG BEETHOVEN</pattern><template><srai>WHO IS BEETHOVEN </srai></template></category>

</aiml>

 

Suppression des attributs :

 

<aiml>

  <category>

    <pattern>BEETHOVEN *</pattern>

    <template>It's amazing that he composed music while deaf.

        <think><set><set>BEETHOVEN</set></set></think></template>

  </category>

  <category>

    <pattern>WHAT WAS THE * BEETHOVEN *</pattern>

    <template>Fidelio.</template>

  </category>

  <category>

    <pattern>WHO IS BEETHOVEN</pattern>

    <template>The dog or the deaf composer?</template>

  </category>

  <category>

    <pattern>WHO IS LUDWIG BEETHOVEN</pattern>

    <template>

      <srai>WHO IS BEETHOVEN </srai>

    </template>

  </category>

</aiml>

 

La balise <set> est présente à plusieurs niveaux, une mise à plat est requise :

aiml\category\template\think\set\set\ <> aiml\category\template\think\set\ -> set__1

 

<aiml>

  <category>

    <pattern>BEETHOVEN *</pattern>

    <template>It's amazing that he composed music while deaf.

        <think><set><set__1>BEETHOVEN</set__1></set></think></template>

  </category>

  ...

</aiml>

 

Extraction du DTD :

 

<!ELEMENT aiml ( category )*>

    <!ELEMENT category ( pattern | template )*>

        <!ELEMENT pattern ( #PCDATA )>

        <!ELEMENT template ( #PCDATA | srai | think )*>

            <!ELEMENT srai ( #PCDATA )>

            <!ELEMENT think ( set )>

                <!ELEMENT set ( set__1 )>

                    <!ELEMENT set__1 ( #PCDATA )>

 

Version non dédoublonnée (pour comparaison seulement, ne fonctionne pas à cause du doublon set) :

 

<!ELEMENT aiml ( category )*>

    <!ELEMENT category ( pattern |

          template )*>

        <!ELEMENT pattern ( #PCDATA )>

        <!ELEMENT template ( #PCDATA |

              srai |

              think )*>

            <!ELEMENT srai ( #PCDATA )>

            <!ELEMENT think ( set )>

                <!ELEMENT set ( set )>

                    <!ELEMENT set ( #PCDATA )> (L'élément global 'set' a déjà été déclaré.)

 

 

Limitations

 

Pour que le schéma extrait soit pertinent, il faut que le document source dont il est extrait soit représentatif des données. C'est d'autant plus vrai en ce qui concerne les balises récursives (ou hiérarchiques) : il sera plus difficile d'obtenir un document capable de représenter toutes les combinaisons possibles, de sorte qu'ensuite, la vérification d'un autre document selon le schéma déduit provoquera des erreurs qui ne seront peut être que des nouveaux cas possibles qui n'avaient pas été prévus dans le document de référence. Il faudra donc faire la part des erreurs dues au manque de représentativité, des vraies erreurs de structure.

 

 

Historique des versions

 

Version 1.01 du 07/09/2008 : Première version

 

 

Liens

 

AliceVB : Interface pour l'AIMLBot : Robot de discussion de type Alice

 

- Xml2Schema : XML to Schema Tool for Visual Basic 2008

  http://msdn2.microsoft.com/vbasic/bb840042.aspx

 

- DTD :

  http://fr.wikipedia.org/wiki/Document_Type_Definition

  http://en.wikipedia.org/wiki/Document_Type_Definition

  www.w3schools.com/dtd

  http://zvon.developpez.com/tutoriels/dtd

 

- Comparaison DTD, XML Schema et autres :

  http://en.wikipedia.org/wiki/XML_Schema_Language_Comparison

 

Schematron : + puissant que schéma :

  http://xml.ascc.net/schematron/

  Because it uses paths rather than grammars, it can be used to assert many constraints that cannot be expressed by DTDs or XML Schemas.

 

RelaxNG : http://www.relaxng.org/

  Forme compact : http://www.relaxng.org/compact-20021121.html

  Intro à RelaxNG : http://www-106.ibm.com/developerworks/library/x-matters25.html

  Validateur JAXP pour RelaxNG : http://www.apache.org/~andyc/neko/doc/relaxng/index.html

  Trang : Multi-format schema converter based on RELAX NG (dont DTD et XML Schema)

  www.thaiopensource.com/relaxng/trang.html

  Déduire un RelaxNG à partir d'une instance XML : http://examplotron.org/