I have two XML files that I want to compare. I am using XMLUnit 2. But elements can be out of order (including sub element ordering) All three examples below need to be considered equivalent:
<Product>
<Property>
<Container value="1">Test 01</Container>
<Container value="3">Test 02</Container>
<Container value="5">Test 03</Container>
</Property>
<Property>
<Container value="2">Test A</Container>
<Container value="4">Test B</Container>
<Container value="6">Test C</Container>
</Property>
</Product>
<Product>
<Property>
<Container value="5">Test 03</Container>
<Container value="1">Test 01</Container>
<Container value="3">Test 02</Container>
</Property>
<Property>
<Container value="2">Test A</Container>
<Container value="4">Test B</Container>
<Container value="6">Test C</Container>
</Property>
<Product>
<Property>
<Container value="2">Test A</Container>
<Container value="4">Test B</Container>
<Container value="6">Test C</Container>
</Property>
<Property>
<Container value="5">Test 03</Container>
<Container value="1">Test 01</Container>
<Container value="3">Test 02</Container>
</Property>
</Product>
It seems like XMLUnit should be able to do this fairly easily but I don't know what I need to do to configure the test. I should add that nesting can get deeper where container has more elements under it that demonstrate the same issue.
---
POSSIBLE SOLUTION:
public class SubNodeEqualityElementSelector implements ElementSelector {
public SubNodeEqualityElementSelector() {
}
@Override
public boolean canBeCompared(Element controlElement, Element testElement) {
// test input nodes for equality first.
if (!ElementSelectors.byNameAndAllAttributes.canBeCompared(controlElement, testElement)) {
return false;
}
if (!ElementSelectors.byNameAndText.canBeCompared(controlElement, testElement)) {
return false;
}
// test children for equality. For each ctrl child, make sure that a matching element is found in the test set.
ArrayList<Element> ctrlChildren = getChildElements(controlElement);
ArrayList<Element> testChildren = getChildElements(testElement);
ArrayList<Element> ctrlChildrenResults = new ArrayList<Element>(ctrlChildren);
ArrayList<Element> testChildrenResults = new ArrayList<Element>(testChildren);
for (Element ctrlChild : ctrlChildren) {
for (Element testChild : testChildren) {
if (ElementSelectors.byNameAndAllAttributes.canBeCompared(ctrlChild, testChild) &&
ElementSelectors.byNameAndText.canBeCompared(ctrlChild, testChild)) {
ctrlChildrenResults.remove(ctrlChild);
testChildrenResults.remove(testChild);
}
}
}
return ctrlChildrenResults.size() == 0 && testChildrenResults.size() == 0;
}
private ArrayList<Element> getChildElements(Element elem) {
ArrayList<Element> retVal = new ArrayList<Element>();
NodeList childNodes = elem.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
if (node instanceof Element){
Element element = (Element) node;
retVal.add(element);
}
}
return retVal;
}
}
I tested the above class (SubNodeEqualityElementSelector) and it seems to work well!
This is a pretty difficult example.
If you want to tackle this with XMLUnit 1.x then you'll have to code up an
ElementQualifier
that picks the correctProperty
element when faced with the list. You'll have to write it yourself, neither of the built-in versions will do. In addition you can only have oneElementQualifier
so you'll also need to take care of the remaining comparisons, for example picking the correctContainer
- something thatElementNameAndAttributeQualifier
could have done.Using XMLUnit 2.x it doesn't get a lot easier, but at least you can use the conditional builder to combine
ElementSelector
. The user guide example in https://github.com/xmlunit/user-guide/wiki/SelectingNodes is close, it picks the correct "outer" element based on the nested text of the first "inner" element. In your case it looks as if you needed to pick theProperty
based on an attribute of the nestedContainer
s, but unfortunately it's not enough to look at the firstContainer
. I'm afraid we won't come up with an XPath that works so you probably still need to write Java code that picks the correctProperty
, but once you've got that, you can do something like