web-dev-qa-db-fra.com

Comparer deux chaînes XML en ignorant l'ordre des éléments

Support J'ai deux chaînes xml

<test>
  <elem>a</elem>
  <elem>b</elem>
</test>

<test>
  <elem>b</elem>
  <elem>a</elem>
</test>

Comment écrire un test qui compare ces deux chaînes et ignore l'ordre des éléments?

Je veux que le test soit aussi court que possible, sans place pour l'analyse XML de 10 lignes, etc. Je recherche une assertion simple ou similaire.

J'ai ceci (qui ne marche pas)

   Diff diff = XMLUnit.compareXML(expectedString, actualString);   
   XMLAssert.assertXMLEqual("meh", diff, true);
16
Roay Spol

Ma réponse originale est obsolète. Si je devais le reconstruire, j'utiliserais xmlunit 2 et xmlunit-matchers. Veuillez noter que pour l'unité xml, un ordre différent est toujours «similaire», pas égal.

@Test
public void testXmlUnit() {
    String myControlXML = "<test><elem>a</elem><elem>b</elem></test>";
    String expected = "<test><elem>b</elem><elem>a</elem></test>";
    assertThat(myControlXML, isSimilarTo(expected).withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText)));
    //In case you wan't to ignore whitespaces add ignoreWhitespace().normalizeWhitespace()
    assertThat(myControlXML, isSimilarTo(expected).ignoreWhitespace().normalizeWhitespace().withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText)));
}  

Si quelqu'un ne veut toujours pas utiliser une implémentation Java pure, c'est le cas. Cette implémentation extrait le contenu du fichier xml et compare la liste en ignorant l'ordre.

public static Document loadXMLFromString(String xml) throws Exception {
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = factory.newDocumentBuilder();
    InputSource is = new InputSource(new StringReader(xml));
    return builder.parse(is);
}

@Test
public void test() throws Exception {
    Document doc = loadXMLFromString("<test>\n" +
            "  <elem>b</elem>\n" +
            "  <elem>a</elem>\n" +
            "</test>");
    XPathFactory xPathfactory = XPathFactory.newInstance();
    XPath xpath = xPathfactory.newXPath();
    XPathExpression expr = xpath.compile("//test//elem");
    NodeList all = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
    List<String> values = new ArrayList<>();
    if (all != null && all.getLength() > 0) {
        for (int i = 0; i < all.getLength(); i++) {
            values.add(all.item(i).getTextContent());
        }
    }
    Set<String> expected = new HashSet<>(Arrays.asList("a", "b"));
    assertThat("List equality without order",
            values, containsInAnyOrder(expected.toArray()));
}
8
user1121883

XMLUnit fera ce que vous voulez, mais vous devez spécifier l'élémentQualifier. Sans élémentQualifier spécifié, seuls les nœuds situés à la même position seront comparés.

Pour votre exemple, vous voulez un ElementNameAndTextQualifer, cela considère un nœud similaire s'il en existe un qui correspond au nom de l'élément et à sa valeur textuelle, quelque chose comme: 

Diff diff = new Diff(control, toTest);
// we don't care about ordering
diff.overrideElementQualifier(new ElementNameAndTextQualifier());
XMLAssert.assertXMLEqual(diff, true);

Vous pouvez en savoir plus à ce sujet ici: http://xmlunit.sourceforge.net/userguide/html/ar01s03.html#ElementQualifier

18
Steve Lancashire

Pour xmlunit 2.0 (je cherchais cela), c'est maintenant fait, en utilisant DefaultNodeMatcher

Diff diff = Diffbuilder.compare(Input.fromFile(control))
   .withTest(Input.fromFile(test))
   .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
   .build()

J'espère que cela aide cela aide d'autres personnes à googler ...

17
Akzidenzgrotesk

OPTION 1
Si le code XML est simple, essayez ceci:

 String testString = ...
 assertTrue(testString.matches("(?m)^<test>(\\s*<elem>(a|b)</elem>\\s*){2}</test>$"));


OPTION 2
Si le XML est plus élaboré, chargez-le avec un analyseur XML et comparez les nœuds réels trouvés avec vos nœuds de référence.

0
Stephan

Pour moi, j’avais également besoin d’ajouter la méthode: checkForSimilar() à la DiffBuilder. Sans cela, l’assertion était une erreur en disant que la séquence des nœuds n’était pas la même même)

Mon code était:

 Diff diff = Diffbuilder.compare(Input.fromFile(control))
   .withTest(Input.fromFile(test))
   .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
   .checkForSimilar()
   .build()
0
pierre

Juste à titre d'exemple pour comparer des éléments xml plus complexes en fonction de l'égalité d'attribut name Par exemple:

<request>
     <param name="foo" style="" type="xs:int"/>
     <param name="Cookie" path="cookie" style="header" type="xs:string" />
</request>

vs.

<request>
     <param name="Cookie" path="cookie" style="header" type="xs:string" />
     <param name="foo" style="query" type="xs:int"/>
</request>

Avec le qualificatif d'élément personnalisé suivant:

final Diff diff = XMLUnit.compareXML(controlXml, testXml);
diff.overrideElementQualifier(new ElementNameAndTextQualifier() {

    @Override
    public boolean qualifyForComparison(final Element control, final Element test) {
        // this condition is copied from super.super class
        if (!(control != null && test != null
                      && equalsNamespace(control, test)
                      && getNonNamespacedNodeName(control).equals(getNonNamespacedNodeName(test)))) {
            return false;
        }

        // matching based on 'name' attribute
        if (control.hasAttribute("name") && test.hasAttribute("name")) {
            if (control.getAttribute("name").equals(test.getAttribute("name"))) {
                return true;
            }
        }
        return false;
    }
});
XMLAssert.assertXMLEqual(diff, true);
0
Stepan Vavra

Écriture croisée à partir de Compare XML en ignorant l'ordre des éléments enfants

J'avais un besoin similaire ce soir et je ne trouvais rien qui corresponde à mes exigences.

Ma solution consistait à trier les deux fichiers XML que je voulais différencier, en ordre alphabétique par nom d'élément. Une fois qu'ils étaient tous les deux dans un ordre cohérent, je pouvais comparer les deux fichiers triés à l'aide d'un outil de comparaison visuelle classique.

Si cette approche semble utile à quelqu'un d'autre, j'ai partagé le script Python que j'ai écrit pour effectuer le tri à l'adresse { http://dalelane.co.uk/blog/?p=3225 } _.

0
dalelane