web-dev-qa-db-fra.com

Comment compareriez-vous deux documents XML?

Dans le cadre de la classe de base de certains tests unitaires approfondis, j'écris une fonction d'assistance qui compare de manière récursive les nœuds d'un objet XmlDocument à un autre en C # (.NET). Quelques exigences de ceci:

  • Le premier document est le source , par exemple. ce que je veux que le document XML ressemble. Ainsi, le second est celui dans lequel je veux trouver des différences et il ne doit pas contenir de nœuds extra pas dans le premier document.
  • Doit jeter une exception lorsque trop de différences significatives sont trouvées, et il devrait être facilement compris par un humain jetant un coup d'oeil à la description.
  • L'ordre des éléments enfants est important, les attributs peuvent être dans n'importe quel ordre.
  • Certains attributs sont ignorables. spécifiquement xsi:schemaLocation et xmlns:xsi, bien que j'aimerais pouvoir indiquer lesquels.
  • Les préfixes des espaces de noms doivent correspondre dans les attributs et les éléments.
  • Les espaces entre les éléments ne sont pas pertinents.
  • Les éléments ou auront des éléments enfants ouInnerText, mais pas les deux.

Pendant que je rédige quelque chose ensemble: Quelqu'un at-il écrit un tel code et serait-il possible de le partager ici?

D'un côté, comment appelleriez-vous les premier et deuxième documents? Je me suis référé à eux comme "source" et "cible", mais je me sens mal puisque le source est ce que je veux que le cible ressemble, sinon je lève une exception.

57
Neil C. Obremski

Microsoft a une XML ​​diff API que vous pouvez utiliser

53
Danimal
6
Andrej Adamenko

essayez XMLUnit . Cette bibliothèque est disponible pour Java et .Net

5

Comparer des documents XML est compliqué. Google pour xmldiff (il existe même une solution Microsoft) pour certains outils. J'ai résolu ce problème de deux manières. J'ai utilisé XSLT pour trier les éléments et les attributs (car ils apparaissaient parfois dans un ordre différent, ce qui ne m'intéressait pas), et filtrer les attributs que je ne voulais pas comparer, puis j'ai utilisé XML: : Diff ou XML :: SemanticDiff module Perl, ou bien imprimé chaque document avec chaque élément et attribut sur une ligne distincte, et en utilisant la ligne de commande Unix avec diff pour les résultats.

5
runrig

Ce code ne satisfait pas toutes vos exigences, mais c'est simple et je l'utilise pour mes tests unitaires. L'ordre des attributs n'a pas d'importance, mais l'ordre des éléments l'est aussi. Le texte interne de l'élément n'est pas comparé. J'ai également ignoré le cas lors de la comparaison des attributs, mais vous pouvez facilement le supprimer.

public bool XMLCompare(XElement primary, XElement secondary)
{
    if (primary.HasAttributes) {
        if (primary.Attributes().Count() != secondary.Attributes().Count())
            return false;
        foreach (XAttribute attr in primary.Attributes()) {
            if (secondary.Attribute(attr.Name.LocalName) == null)
                return false;
            if (attr.Value.ToLower() != secondary.Attribute(attr.Name.LocalName).Value.ToLower())
                return false;
        }
    }
    if (primary.HasElements) {
        if (primary.Elements().Count() != secondary.Elements().Count())
            return false;
        for (var i = 0; i <= primary.Elements().Count() - 1; i++) {
            if (XMLCompare(primary.Elements().Skip(i).Take(1).Single(), secondary.Elements().Skip(i).Take(1).Single()) == false)
                return false;
        }
    }
    return true;
}
4
Two Cents

Une autre façon de faire serait: 

  1. Obtenez le contenu des deux fichiers en deux chaînes différentes. 
  2. Transformez les chaînes en utilisant un XSLT (qui copiera simplement le tout sur deux nouvelles chaînes). Cela garantira que tous les espaces en dehors des éléments sont supprimés. Cela donnera deux nouvelles chaînes. 
  3. Maintenant, comparez les deux chaînes l'une avec l'autre. 

Cela ne vous donnera pas l'emplacement exact de la différence, mais si vous voulez simplement savoir s'il y a une différence, il est facile de le faire sans bibliothèques tierces.

3
Do Will

J'utilise ExamXML pour comparer des fichiers XML. Vous pouvez l'essayer .. Les auteurs, A7Soft, fournissent également une API permettant de comparer des fichiers XML.

3
Alex Gulin

https://github.com/CameronWills/FatAntelope Une autre bibliothèque alternative à l'API Microsoft XML Diff. Il dispose d'un algorithme de diff XML pour effectuer une comparaison non ordonnée de deux documents XML et produire une correspondance optimale.

C'est un port C # de l'algorithme X-Diff décrit ici: http://pages.cs.wisc.edu/~yuanwang/xdiff.html

Disclaimer: Je l'ai écrit :)

3
cwills

Non pertinent pour l'OP puisqu'il ignore actuellement l'ordre des enfants, mais si vous voulez une solution avec code uniquement, vous pouvez essayer XmlSpecificationCompare que j'ai plutôt mal orienté développé.

2
Eli Algranti

J'ai écrit cette feuille de comparaison basée sur XSLT 1.0 que vous comparez b.xml à l'entrée XML qui affiche les différences d'éléments dans l'entrée qui ne sont pas dans b.xml.

https://github.com/sflynn1812/xslt-diff

0
Stephen Flynn

Pour comparer deux sorties XML lors de tests automatisés, j'ai trouvé XNode.DeepEquals.

Compare les valeurs de deux nœuds, y compris les valeurs de tous les nœuds descendants.

Usage:

var xDoc1 = XDocument.Parse(xmlString1);
var xDoc2 = XDocument.Parse(xmlString2);

bool isSame = XNode.DeepEquals(xDoc1.Document, xDoc2.Document);
//Assert.IsTrue(isSame);

Référence: https://docs.Microsoft.com/en-us/dotnet/api/system.xml.linq.xnode.deepequals?view=netcore-2.2

0
Mirek

Basé sur @Two Cents et utilisant ce lien XMLSorting i a créé mon propre XmlComparer 

Comparer le programme XML

private static bool compareXML(XmlNode node, XmlNode comparenode)
    {

        if (node.Value != comparenode.Value)
            return false;

            if (node.Attributes.Count>0)
            {
                foreach (XmlAttribute parentnodeattribute in node.Attributes)
                {
                    string parentattributename = parentnodeattribute.Name;
                    string parentattributevalue = parentnodeattribute.Value;
                    if (parentattributevalue != comparenode.Attributes[parentattributename].Value)
                    {
                        return false;
                    }

                }

            }

          if(node.HasChildNodes)
            {
            sortXML(comparenode);
            if (node.ChildNodes.Count != comparenode.ChildNodes.Count)
                return false;
            for(int i=0; i<node.ChildNodes.Count;i++)
                {

                string name = node.ChildNodes[i].LocalName;
                if (compareXML(node.ChildNodes[i], comparenode.ChildNodes[i]) == false)
                    return false;
                }

            }



        return true;
    }

Trier le programme XML

 private static void sortXML(XmlNode documentElement)
    {
        int i = 1;
        SortAttributes(documentElement.Attributes);
        SortElements(documentElement);
        foreach (XmlNode childNode in documentElement.ChildNodes)
        {
            sortXML(childNode);

        }
    }



  private static void SortElements(XmlNode rootNode)
    {



            for(int j = 0; j < rootNode.ChildNodes.Count; j++) {
                for (int i = 1; i < rootNode.ChildNodes.Count; i++)
                {
                    if (String.Compare(rootNode.ChildNodes[i].Name, rootNode.ChildNodes[1 - 1].Name) < 0)
                    {
                        rootNode.InsertBefore(rootNode.ChildNodes[i], rootNode.ChildNodes[i - 1]);

                    }


                }
            }
           // Console.WriteLine(j++);


    }
 private static void SortAttributes(XmlAttributeCollection attribCol)
    {
        if (attribCol == null)
            return;
        bool changed = true;
        while (changed)
        {
            changed = false;
            for (int i = 1; i < attribCol.Count; i++)
        {
                if (String.Compare(attribCol[i].Name, attribCol[i - 1].Name) < 0)
                {
                    //Replace
                    attribCol.InsertBefore(attribCol[i], attribCol[i - 1]);
                    changed = true;

                }
            }
        }
    }
0
Chetan Mehra