Java compare XML tags with different names

291 Views Asked by At

I have two XML's which have basically the same content but a single tag has a different name:

actualSource:

<ns2:Message xmlns:ns2="http://blabla.blabla.com">
    <data>
        <fieldabc>aaaa</fieldabc>
    </data>
    <asset>
        <field1>50.85</field1>
        <field2>Scooter</field2>
        <field3>Small</field3>
    </asset>

expectedSource:

<ns2:Message xmlns:ns2="http://different.blabla.com">
    <data>
        <fieldabc>aaaa</fieldabc>
    </data>
    <product>
        <field1>50.85</field1>
        <field2>Scooter</field2>
        <field3>Small</field3>
    </product>
</ns2:Message>

The 'asset' and 'product' tags have same content, same fields name. I am using xmlunit:

    <dependency>
        <groupId>org.xmlunit</groupId>
        <artifactId>xmlunit-core</artifactId>
        <version>2.7.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.xmlunit</groupId>
        <artifactId>xmlunit-matchers</artifactId>
        <version>2.7.0</version>
        <scope>test</scope>
    </dependency>

I am trying to compare the via DiffBuilder:

Diff diff = DiffBuilder.compare(expectedSource).withTest(actualSource)
                .ignoreWhitespace()
                .withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndText))
                .checkForSimilar()
                .build()

In differences I receive:

Expected child 'null' but was '{http://blabla.blabla.com}Message' - comparing <NULL> to <ns2:Message...> at /Message[1] (DIFFERENT)
Expected child '{http://different.blabla.com}Message' but was 'null' - comparing <ns2:Message...> at /Message[1] to <NULL> (DIFFERENT)

I need to compare those 2 messages if they are equal without unmarshalling them to java objects, I need to assert pure XML's.

1

There are 1 best solutions below

0
Stefan Bodewig On

ElementSelectors.byNameAndText has not considered your two Message elements as candidates for comparison as it is sensitive to XML namespace URIs. This is true for all built-in ElementSelectors. From an XML perspective the two elements are completely different things.

You need to provide an ElementSelector implementation that only looks at the local name of your elements. Something like

    @Override
    public boolean canBeCompared(final Element controlElement,
                                 final Element testElement) {
        if (controlElement == null || testElement == null) {
            return false;
        }
        final String controlLocal = controlElement.getLocalName();
        final String controlName = controlLocal == null ? controlElement.getNodeName() : controlLocal;
        final String testLocal = testElement.getLocalName();
        final String testName = testLocal == null ? testElement.getNodeName() : testLocal;
        return controlName.equals(testName);
    }

would be a namespace ignoring version of byName.

Using this will not be enough, though, as you'll get differences of type ComparisonType.NAMESPACE_URI which will make your documents DIFFERENT. In order to get rid of them you must also override the DifferenceEvaluator. The easiest solution is to use DifferenceEvaluators.downgradeDifferencesToSimilar(ComparisonType.NAMESPACE_URI).

With a combination like this you basically remove namespace sensitivity from XMLUnit's difference engine.

Personally I'd recommend fixing the cause of the difference in namespace URIs, though. I'm afraid it is going to byte you sooner or later as namespace URIs are all but irrelevant to a lot of XML processing tools. But you will certainly know your case a lot better than I do.