javaxmljava-8diffxmlunit-2

XmlUnit ignore order of elements when comparing XML files


I have the two following xml files:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pricebooks xmlns="http://www.blablabla.com">
    <pricebook>
        <header pricebook-id="my-id">
            <currency>GBP</currency>
            <display-name xml:lang="x-default">display name</display-name>
            <description>my description 1</description>
        </header>
        <price-tables>
            <price-table product-id="id1" mode="mode1">
                <amount quantity="1">30.00</amount>
            </price-table>
            <price-table product-id="id2" mode="mode2">
                <amount quantity="1">60.00</amount>
            </price-table>
        </price-tables>
    </pricebook>
</pricebooks>

and

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pricebooks xmlns="http://www.blablabla.com">
    <pricebook>
        <header pricebook-id="my-id">
            <currency>GBP</currency>
            <display-name xml:lang="x-default">display name</display-name>
            <description>my description 1</description>
        </header>
        <price-tables>
            <price-table product-id="id2" mode="mode2">
                <amount quantity="1">60.00</amount>
            </price-table>
            <price-table product-id="id1" mode="mode1">
                <amount quantity="1">30.00</amount>
            </price-table>
        </price-tables>
    </pricebook>
</pricebooks>

Which I'm trying to compare ignoring the order of the elements price-table, so for me those two are equal. I'm using

        <dependency>
            <groupId>org.xmlunit</groupId>
            <artifactId>xmlunit-core</artifactId>
            <version>2.5.0</version>
        </dependency>

and the code is the following, but I'm not able to make it work. It complains because the attribute values id1 and id2 are different.

Diff myDiffSimilar = DiffBuilder
    .compare(expected)
    .withTest(actual)
    .checkForSimilar()
    .ignoreWhitespace()
    .ignoreComments()
    .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
    .build();
assertFalse(myDiffSimilar.hasDifferences());

I have also tried to edit the the nodeMatcher as follow:

.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.conditionalBuilder()
    .whenElementIsNamed("price-tables")
    .thenUse(ElementSelectors.byXPath("./price-table", ElementSelectors.byNameAndText))
    .elseUse(ElementSelectors.byName)
    .build()))

Thank you for your help.


Solution

  • I don't see any nested text inside your price-table elements at all, so byNamAndText matches on element names only - which will be the same for all price-tables and thus not do what you want.

    In you example there is no ambiguity for price-tables as there is only one anyway. So the byXPath approach looks wrong. At least in your snippet XMLUnit should do fine with byName except for the price-table elements.

    I'm not sure whether product-id alone is what identifies your price-table elements or the combination of all attributes. Either byNameAndAttributes("product-id") or its byNameAndAllAttributes cousin should work.

    If it is only product-id then byNameAndAttributes("product-id") becomes byName for all elements that don't have any product-id attribute at all. In this special case byNameAndAttribute("product-id") alone will work for your whole document as we can see it - more or less by accident.

    If you need more complex rules for other elements than price-table or you want to make things more explicit than

    ElementSelectors.conditionalBuilder()
        .whenElementIsNamed("price-table")
        .thenUse(ElementSelectors.byNameAndAttributes("product-id"))
        // more special cases
        .elseUse(ElementSelectors.byName)
    

    is the better choice.