How do I ignore certain elements when comparing XML?
Asked Answered
S

5

43

I have an XML message like so:

<root>
  <elementA>something</elementA>
  <elementB>something else</elementB>
  <elementC>yet another thing</elementC>
</root>

I want to compare a message of this type produced by a method under test to an expected message, but I don't care about elementA. So, I'd like the above message to be considered equal to:

<root>
  <elementA>something different</elementA>
  <elementB>something else</elementB>
  <elementC>yet another thing</elementC>
</root>

I'm using the latest version of XMLUnit.

I'm imagining that the answer involves creating a custom DifferenceListener; I just don't want to reinvent the wheel if there's something ready to use out there.

Suggestions that use a library other than XMLUnit are welcome.

Seibert answered 6/8, 2009 at 21:27 Comment(0)
D
33

Things have changed a lot for XMLUnit since this question was answered.

You can now easily ignore a node when using a DiffBuilder:

final Diff documentDiff = DiffBuilder
            .compare(expectedSource)
            .withTest(actualSource)
            .withNodeFilter(node -> !node.getNodeName().equals(someName))
            .build();

If you then call documentDiff.hasDifferences() nodes added to filter will be ignored.

Defalcate answered 25/5, 2016 at 15:56 Comment(1)
Looks good - but how to use ElementNameAndAttributeQualifier with DiffBuilder ??? to allow different order of xml elements?Hogweed
S
41

I wound up implementing a DifferenceListener that takes a list of node names (with namespaces) to ignore textual differences for:

public class IgnoreNamedElementsDifferenceListener implements DifferenceListener {
    private Set<String> blackList = new HashSet<String>();

    public IgnoreNamedElementsDifferenceListener(String ... elementNames) {
        for (String name : elementNames) {
            blackList.add(name);
        }
    }

    public int differenceFound(Difference difference) {
        if (difference.getId() == DifferenceConstants.TEXT_VALUE_ID) {
            if (blackList.contains(difference.getControlNodeDetail().getNode().getParentNode().getNodeName())) {
                return DifferenceListener.RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
            }
        }

        return DifferenceListener.RETURN_ACCEPT_DIFFERENCE;
    }

    public void skippedComparison(Node node, Node node1) {

    }
}
Seibert answered 6/8, 2009 at 23:29 Comment(2)
I'm surprised it's this complex - disappointing if xmlunit does not have something built in for ignoring specific elements?Lahdidah
This solution is quite old now. Apparently the new xmlunit programming model uses ComparisonListener, not DifferenceListener.Coray
D
33

Things have changed a lot for XMLUnit since this question was answered.

You can now easily ignore a node when using a DiffBuilder:

final Diff documentDiff = DiffBuilder
            .compare(expectedSource)
            .withTest(actualSource)
            .withNodeFilter(node -> !node.getNodeName().equals(someName))
            .build();

If you then call documentDiff.hasDifferences() nodes added to filter will be ignored.

Defalcate answered 25/5, 2016 at 15:56 Comment(1)
Looks good - but how to use ElementNameAndAttributeQualifier with DiffBuilder ??? to allow different order of xml elements?Hogweed
I
9

I would use XSLT and the identity transform to filter out elements I want to ignore, and compare the results.

See XSL: how to copy a tree, but removing some nodes ? earlier on SO.

Isobaric answered 6/8, 2009 at 23:18 Comment(0)
W
6

You can now try ${xmlunit.ignore} in XMLUnit 2.6.0 (add dependency xmlunit-placeholders). Sample code is as below.

Diff diff = DiffBuilder
    .compare(expectedXML)
    .withTest(actualXML)
    .withDifferenceEvaluator(new PlaceholderDifferenceEvaluator())
    .build();

Expected XML:

<root>
  <elementA>${xmlunit.ignore}</elementA>
  <elementB>something else</elementB>
  <elementC>yet another thing</elementC>
</root>

Actual XML:

<root>
  <elementA>anything</elementA>
  <elementB>something else</elementB>
  <elementC>yet another thing</elementC>
</root>

Do notice that currently the ${xmlunit.ignore} only supports text node and attribute value ignorance, as can be seen from the unit tests.

Whitehouse answered 26/6, 2018 at 6:38 Comment(0)
H
0

Lets make it simple, to ignore nodes irrespective of length of how many nodes you want to ignore or if you dont know how many number of nodes you want to ignore, follow these simple steps

This is just a XMLdiff method with two xml's as a string

public static void XMLdiff(String XML1, String XML2){
               
 //nodes you wanted to ignore
        String[] ignoreNodes = {"nodeX", "nodeY", "nodeZ"};
      
//testnode(node,ignoreNodes) is handled below for finding and ignoring multiple nodes
        final Diff documentDiff = DiffBuilder.compare(XML1)
                                  .withTest(XML2)
                                  .withNodeFilter(node->testnode(node,ignoreNodes))
                                  .build();
}

private static boolean testnode(Node node, String[] name) {

        for (int i = 0; i < name.length; i++) {

            if (node.getNodeName().toString().equals(name[i])) {
                return false;
            }
            if (name.length == 0) {
                break;
            }
        }
        return true;
    }

add dependencies or jar from this link https://www.xmlunit.org/

Halloran answered 23/6, 2021 at 12:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.