javaxmlunit

XMLUnit compare XML not working where parent node contains multiple child nodes of same name and attributes list but different attribute values


I am using XMLUnit to compare two XMLs to detect the updates. My pom is as :

<dependency>
    <groupId>xmlunit</groupId>
    <artifactId>xmlunit</artifactId>
    <version>1.6</version>
</dependency>

For most of the scenarios, XMLUnit is working fine except few where the given differences are wrong or unexpected. Lets take first the positive scenario where the XMLUnit is working correct :

Case-1:

XML-Original:

<ClassHours>                          
    <Hours close="11:10" day="27" classname="sec-1" open="17:20" noOfStudent="23"/>
    <Hours close="11:10" day="28" classname="sec-2" open="17:20" noOfStudent="43"/>
    <Hours close="11:10" day="21" classname="sec-3" open="17:20" noOfStudent="12"/>
    <Hours close="11:10" day="1" classname="sec-4" open="17:20" noOfStudent="54"/>
    <Hours close="11:10" day="25" classname="sec-5" open="17:20" noOfStudent="22"/>
    <Hours close="11:10" day="1" classname="sec-6" open="17:20" noOfStudent="10"/>      s
</ClassHours>

XML-Updated:

<ClassHours>                          
    <Hours close="18:00" day="27" classname="sec-1" open="10:00" noOfStudent="23"/>
    <Hours close="18:00" day="28" classname="sec-2" open="10:00" noOfStudent="43"/>
    <Hours close="18:00" day="21" classname="sec-3" open="10:00" noOfStudent="12"/>
    <Hours close="18:00" day="1" classname="sec-4" open="10:00" noOfStudent="54"/>
    <Hours close="18:00" day="25" classname="sec-5" open="10:00" noOfStudent="22"/>
    <Hours close="18:00" day="1" classname="sec-6" open="10:00" noOfStudent="10"/>      s
</ClassHours>

as you can notice, the parent node contains multiple child nodes . For all the child nodes, the name and attribute is same and the only difference is in Open and Close attributes value. In this case the output is :

2019-12-24 19:53:28,093  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [close], Before value : [11:10], After value : [18:00]]
2019-12-24 19:53:28,093  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [open], Before value : [17:20], After value : [10:00]]
2019-12-24 19:53:28,093  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [close], Before value : [11:10], After value : [18:00]]
2019-12-24 19:53:28,093  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [open], Before value : [17:20], After value : [10:00]]
2019-12-24 19:53:28,093  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [close], Before value : [11:10], After value : [18:00]]
2019-12-24 19:53:28,095  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [open], Before value : [17:20], After value : [10:00]]
2019-12-24 19:53:28,096  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [close], Before value : [11:10], After value : [18:00]]
2019-12-24 19:53:28,096  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [open], Before value : [17:20], After value : [10:00]]
2019-12-24 19:53:28,096  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [close], Before value : [11:10], After value : [18:00]]
2019-12-24 19:53:28,096  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [open], Before value : [17:20], After value : [10:00]]
2019-12-24 19:53:28,097  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [close], Before value : [11:10], After value : [18:00]]
2019-12-24 19:53:28,097  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [open], Before value : [17:20], After value : [10:00]]

Case-2:

XML-Original:

<ClassHours>                          
    <Hours close="11:10" day="27" classname="sec-1" open="17:20" noOfStudent="23"/>
    <Hours close="11:10" day="28" classname="sec-2" open="17:20" noOfStudent="43"/>
    <Hours close="11:10" day="21" classname="sec-3" open="17:20" noOfStudent="12"/>
    <Hours close="11:10" day="1" classname="sec-4" open="17:20" noOfStudent="54"/>
    <Hours close="11:10" day="25" classname="sec-5" open="17:20" noOfStudent="22"/>
    <Hours close="11:10" day="1" classname="sec-6" open="17:20" noOfStudent="10"/>      s
</ClassHours>

XML-Updated:

<ClassHours>    
    <Hours close="18:00" day="28" classname="sec-2" open="10:00" noOfStudent="43"/>
    <Hours close="18:00" day="21" classname="sec-3" open="10:00" noOfStudent="12"/>
    <Hours close="18:00" day="27" classname="sec-1" open="10:00" noOfStudent="23"/>
    <Hours close="18:00" day="1" classname="sec-4" open="10:00" noOfStudent="54"/>
    <Hours close="18:00" day="25" classname="sec-5" open="10:00" noOfStudent="22"/>
    <Hours close="18:00" day="1" classname="sec-6" open="10:00" noOfStudent="10"/>      s
</ClassHours>

Here, again for all the child nodes, the name and attribute is same and the difference is in Open and Close attributes value. This time the sequence of the nodes is also updated as classname sec-3 row is shifted after sec-2. In this case the output is :

2019-12-24 19:54:31,737  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [classname], Before value : [sec-1], After value : [sec-2]]
2019-12-24 19:54:31,738  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [close], Before value : [11:10], After value : [18:00]]
2019-12-24 19:54:31,738  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [day], Before value : [27], After value : [28]]
2019-12-24 19:54:31,738  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [noOfStudent], Before value : [23], After value : [43]]
2019-12-24 19:54:31,738  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [open], Before value : [17:20], After value : [10:00]]
2019-12-24 19:54:31,740  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [classname], Before value : [sec-2], After value : [sec-3]]
2019-12-24 19:54:31,740  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [close], Before value : [11:10], After value : [18:00]]
2019-12-24 19:54:31,740  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [day], Before value : [28], After value : [21]]
2019-12-24 19:54:31,741  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [noOfStudent], Before value : [43], After value : [12]]
2019-12-24 19:54:31,741  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [open], Before value : [17:20], After value : [10:00]]
2019-12-24 19:54:31,741  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [classname], Before value : [sec-3], After value : [sec-1]]
2019-12-24 19:54:31,741  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [close], Before value : [11:10], After value : [18:00]]
2019-12-24 19:54:31,741  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [day], Before value : [21], After value : [27]]
2019-12-24 19:54:31,741  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [noOfStudent], Before value : [12], After value : [23]]
2019-12-24 19:54:31,742  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [open], Before value : [17:20], After value : [10:00]]
2019-12-24 19:54:31,742  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [close], Before value : [11:10], After value : [18:00]]
2019-12-24 19:54:31,742  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [open], Before value : [17:20], After value : [10:00]]
2019-12-24 19:54:31,742  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [close], Before value : [11:10], After value : [18:00]]
2019-12-24 19:54:31,742  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [open], Before value : [17:20], After value : [10:00]]
2019-12-24 19:54:31,742  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [close], Before value : [11:10], After value : [18:00]]
2019-12-24 19:54:31,742  INFO [main] (XMLDiffMain.java:107) -  Node [/ClassHours/Hours, node attribute : [open], Before value : [17:20], After value : [10:00]]

here the output is wrong as the difference is in only in values, but XMLUnit is considering the sequence of the nodes as well and giving such output.

My code is :

    LOGGER.debug("Comparing Actual and Expected XMLs");
    XMLUnit.setIgnoreWhitespace(true);
    XMLUnit.setIgnoreAttributeOrder(true);
    XMLUnit.setIgnoreComments(true);
    DetailedDiff diff = new DetailedDiff(XMLUnit.compareXML(expectedXML, actualXML));
    diff.overrideElementQualifier(new ElementNameAndAttributeQualifier());

    return diff.getAllDifferences();

Can somebody tell me how I can fix this? I have also tried adding

diff.overrideDifferenceListener(new IgnoreTextAndAttributeValuesDifferenceListener());
diff.overrideElementQualifier(new RecursiveElementNameAndTextQualifier());

and still getting same results. Also I cannot control the sequence of the XML nodes as it is coming from some other external system. I need to get the fix to ignore sequence while comparing the nodes attributes.

Also, I already mentioned that this is the only case where XMLUnit is not working. For all other cases it is working as expected.


Solution

  • You are using the no-arg constructor of ElementNameAndAttributeQualifier which means two elements are eligible to being compared if their names are the same and all their attributes have the same values. This is not true for any pair of elements in your example as the close attributes are all different. So there are no matches at all.

    In XMLUnit 1.x the default behavior is to match elements that haven't got a matching partner to match them in document order with those without partners from the test document. So in effect you are comparing elements in order.

    One thing you can do is make the ElementNameAndAttributeQualifier more specific by listing the attributes that you expect to keep the same values in the constructor. You probably also want to set XMLUnit.setCompareUnmatched to false so you won't trip over the bad default - or better, yet, switch to XMLUnit 2.x which unlike 1.x is actively maintained and will never compare unmatched nodes to each other.