XML comparison tutorial using XMLUnit

A few days back I received an enquiry asking if there was a ready-to-use tool to facilitate xml document comparison. A quick google search revealed that such a tool namely XMLUnit Version 1.3 did exist in the open source domain. I was able to satisfy the inquirer’s requirements; however a quick glance thru the XMLUnit’s documentation got me curious around the capabilities of the tool and its boundary conditions. This post is primarily to highlight its features and how best to use to achieve optimum results.

Let’s start the basics of comparison between two xml documents.

Consider two xmls, reference.xml and comparison.xml. Initially both the xmls will have exactly the same xml structure.

reference.xml

<?xml version="1.0" encoding="UTF-8"?>
<books>
	<book>
		<name>Angels &amp; Demons</name>
		<isbn>9971-5-0210-0</isbn>
		<author>Dan Brown</author>
		<category></category>
	</book>
	<book>
		<name>You can win</name>
		<isbn>9971-5-0222-0</isbn>
		<author>Shiv Khera</author>
	</book>
</books>

Run the following test client ComparisonTest class to match the two documents.

package com.xmlunit.tutorial.comparison;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.util.List;

import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.Difference;
import org.xml.sax.SAXException;

public class ComparisonTest {

	public static void main(String[] args) {
		URL url1 = ComparisonTest.class.getResource("reference.xml");
		URL url2 = ComparisonTest.class.getResource("comparison.xml");
		FileReader fr1 = null;
		FileReader fr2 = null;
		try {
			fr1 = new FileReader(url1.getPath());
			fr2 = new FileReader(url2.getPath());
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		try {
			Diff diff = new Diff(fr1, fr2);
			System.out.println("Similar? " + diff.similar());
			System.out.println("Identical? " + diff.identical());

			DetailedDiff detDiff = new DetailedDiff(diff);
			List differences = detDiff.getAllDifferences();
			for (Object object : differences) {
				Difference difference = (Difference)object;
				System.out.println("***********************");
				System.out.println(difference);
				System.out.println("***********************");
			}

		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

The XMLUnit’s Diff class is more than satisfactory in identifying differences between two documents; however if the end user is interested in gathering more detailed information around the dissimilarities between the documents, DetailedDiff class must be used. The technique to use both the classes is illustrated in the test class code shown above.

The test client output is

Similar? trueIdentical? true

Essentially informing us that the two documents are same. Let’s make a minor change to the comparison.xml document.

Replace

<category></category>

with

<category/>

The test client output is

Similar? true
Identical? true

So XMLUnit is able to understand that an empty-element tag and a element with start and end tag without any content has the same representation in the XML context. To understand the subtlety between similar and identical, let’s introduce the following structural change in the comparison.xml document.

Change

		<author>Dan Brown</author>
		<category></category>

to

		<category></category>
		<author>Dan Brown</author>

Basically interchange the positions of the author and category elements in the xml document.

The test client output is

Similar? true
Identical? false
***********************
Expected sequence of child nodes '5' but was '7' - comparing <author...> at /books[1]/book[1]/author[1] to <author...> at /books[1]/book[1]/author[1]
***********************
***********************
Expected sequence of child nodes '7' but was '5' - comparing <category...> at /books[1]/book[1]/category[1] to <category...> at /books[1]/book[1]/category[1]
***********************

XMLUnit analyzes the two documents and recognizes that they are similar in structure i.e. the positions of the category and author elements have been interchanged, however due to postional differences, they are not the same, hence not identical. Thus, XMLUnit is able to assist in programmatically determing sutle but conspicuous differences between two xml documents.

Finally, change the comparison.xml to the following structure:

<?xml version="1.0" encoding="UTF-8"?>
<books>
	<book>
		<name>Angels &amp; Demons</name>
		<isbn>9971-5-0210-0</isbn>
		<author>Dan Brown</author>
		<category/>
		<nationality>American</nationality>
	</book>
	<book>
		<name>You can win</name>
		<isbn>9971-5-0222-0</isbn>
		<author>Shiv Khera</author>
	</book>
</books>

The test client output is

Similar? false
Identical? false
***********************
Expected number of child nodes '9' but was '11' - comparing <book...> at /books[1]/book[1] to <book...> at /books[1]/book[1]
***********************
***********************
Expected text value '
	' but was '
		' - comparing <book ...>
	</book> at /books[1]/book[1]/text()[5] to <book ...>
		</book> at /books[1]/book[1]/text()[5]
***********************
***********************
Expected presence of child node 'null' but was 'nationality' - comparing  at null to <nationality...> at /books[1]/book[1]/nationality[1]
***********************
***********************
Expected presence of child node 'null' but was '#text' - comparing  at null to <book ...>
	</book> at /books[1]/book[1]/text()[6]
***********************

Moving on let’s look at other features. Let’s consider two xmls, input.xml and compare.xml

input.xml

<?xml version="1.0" encoding="UTF-8"?>
<books>
	<book>
		<name>Angels &amp; Demons</name>
		<isbn>9971-5-0210-0</isbn>
		<author>Dan Brown</author>
		<category></category>
	</book>
	<book>
		<name>You can win</name>
		<isbn>9971-5-0222-0</isbn>
		<author>Shiv Khera</author>
	</book>
</books>

compare.xml

<?xml version="1.0" encoding="UTF-8"?>
<books>
<!-- My favorite book collection -->
	<book>
		<name>Angels &amp; Demons</name>
		<isbn>9971-5-0210-0</isbn>
		<author>Dan Brown</author>
		<category></category>
	</book>
	<book>
		<name>You can win</name>
		<isbn>9971-5-0222-0</isbn>
		<author>Shiv Khera</author>
	</book>
</books>

Both the xmls are structurally the same except for the comment tag in the compare.xml. The test class to run XMLUnit comparison is found below:

package com.xmlunit.tutorial.comment;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.util.List;

import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.Difference;
import org.custommonkey.xmlunit.XMLUnit;
import org.xml.sax.SAXException;

public class CommentDiffTest {

	public static void main(String[] args) {
		URL url1 = CommentDiffTest.class.getResource("input.xml");
		URL url2 = CommentDiffTest.class.getResource("compare.xml");
		FileReader fr1 = null;
		FileReader fr2 = null;
		try {
			fr1 = new FileReader(url1.getPath());
			fr2 = new FileReader(url2.getPath());
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		//Add init code here.

		try {
			Diff diff = new Diff(fr1, fr2);
			System.out.println(diff.similar());
			System.out.println(diff.identical());

			DetailedDiff detDiff = new DetailedDiff(diff);
			List differences = detDiff.getAllDifferences();
			for (Object object : differences) {
				Difference difference = (Difference)object;
				System.out.println("***********************");
				System.out.println(difference);
				System.out.println("***********************");
			}

		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

The output observed is

Similar? false
Identical? false
***********************
Expected number of child nodes '5' but was '7' - comparing <books...> at /books[1] to <books...> at /books[1]
***********************
***********************
Expected text value '
	' but was '
' - comparing <books ...>
	</books> at /books[1]/text()[1] to <books ...>
</books> at /books[1]/text()[1]
***********************
***********************
Expected sequence of child nodes '1' but was '3' - comparing <book...> at /books[1]/book[1] to <book...> at /books[1]/book[1]
***********************
***********************
Expected sequence of child nodes '3' but was '5' - comparing <book...> at /books[1]/book[2] to <book...> at /books[1]/book[2]
***********************
***********************
Expected text value '
' but was '
	' - comparing <books ...>
</books> at /books[1]/text()[3] to <books ...>
	</books> at /books[1]/text()[3]
***********************
***********************
Expected presence of child node 'null' but was '#comment' - comparing  at null to <!-- My favorite book collection --> at /books[1]/comment()[1]
***********************
***********************
Expected presence of child node 'null' but was '#text' - comparing  at null to <books ...>
</books> at /books[1]/text()[4]
***********************

Frankly it is irritating if you are comparing two documents and some useful comments are forcing the comparator to inform you that the two documents are different. Fortunately, XMLUnit does provide a way to get around this problem. Add the following piece of code in the CommentDiffTest class code at line with the comment //Add init code here.

XMLUnit.setIgnoreComments(Boolean.TRUE);

The test class output is

Similar? false
Identical? false
***********************
Expected text value '
	' but was '

	' - comparing <books ...>
	</books> at /books[1]/text()[1] to <books ...>

	</books> at /books[1]/text()[1]
***********************

The difference message around the comment tag has disappeared but we are still left with this weird difference message. There are two ways to get around this, first change the compare document to the following:

<?xml version="1.0" encoding="UTF-8"?>
<books><!-- My favorite book collection -->
	<book>
		<name>Angels &amp; Demons</name>
		<isbn>9971-5-0210-0</isbn>
		<author>Dan Brown</author>
		<category></category>
	</book>
	<book>
		<name>You can win</name>
		<isbn>9971-5-0222-0</isbn>
		<author>Shiv Khera</author>
	</book>
</books>

The comment is no longer on a new line. The error message was generated by the additional new line.

The test client output is

Similar? true
Identical? true

Alternatively, and this is a preferred solution; add the following line below the XMLUnit.setIgnoreComments… line.

		XMLUnit.setIgnoreWhitespace(Boolean.TRUE);

The compare document can be reverted back to its earlier form and the test client output is

Similar? true
Identical? true

Sometimes xml documents are constructed by business applications without human intervention. Due upstream omissions, sometimes unnecessary white spaces may be added to the xml element content. This could lead to undesirable failure in the document comparison. For e.g. consider the following two xml documents

input1.xml

<?xml version="1.0" encoding="UTF-8"?>
<books>
	<book>
		<name>Angels &amp; Demons</name>
		<isbn>9971-5-0210-0</isbn>
		<author>Dan Brown</author>
	</book>
</books>

input2.xml

<?xml version="1.0" encoding="UTF-8"?>
<books>
	<book>
		<name>Angels &amp; Demons</name>
		<isbn>9971-5-0210-0</isbn>
		<author>  Dan   Brown   </author>
	</book>
</books>

Here both the documents are identical except for the excess whitespaces within the author element of input2.xml document. XMLUnit provides an approach to get around this problem. Consider the following test client code:

package com.xmlunit.tutorial.whitespace;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.util.List;

import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.Difference;
import org.custommonkey.xmlunit.XMLUnit;
import org.xml.sax.SAXException;

public class WhiteSpaceDiffTest {

	public static void main(String[] args) {
		URL url1 = WhiteSpaceDiffTest.class.getResource("input1.xml");
		URL url2 = WhiteSpaceDiffTest.class.getResource("input2.xml");
		FileReader fr1 = null;
		FileReader fr2 = null;
		try {
			fr1 = new FileReader(url1.getPath());
			fr2 = new FileReader(url2.getPath());
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		XMLUnit.setNormalizeWhitespace(Boolean.TRUE);

		try {
			Diff diff = new Diff(fr1, fr2);
			System.out.println("Similar? " + diff.similar());
			System.out.println("Identical? " + diff.identical());

			DetailedDiff detDiff = new DetailedDiff(diff);
			List differences = detDiff.getAllDifferences();
			for (Object object : differences) {
				Difference difference = (Difference)object;
				System.out.println("***********************");
				System.out.println(difference);
				System.out.println("***********************");
			}

		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
}

Refer the line 29 in the WhiteSpaceDiffTest class. By setting the normalizeWhitespace flag, all whitespaces within the xml element character content is replaced with a single space. It also trims the resulting character content at both the ends.

The client output generated is

Similar? true
Identical? true

In case the character content of a xml element is text in one of the xmls and CDATA in other, XMLUnit provides a facility to ignore this difference and treat both the documents as same. Refer the two xml documents and the corresponding test class. Note that the character content within CDATA tag is compared for equality with the text node within the other xml.

<?xml version="1.0" encoding="UTF-8"?>
<books>
	<book>
		<name>Angels &amp; Demons</name>
		<isbn>9971-5-0210-0</isbn>
		<author>Dan Brown</author>
		<reviewcomment>This book is wonderful. Highly recommended.</reviewcomment>
	</book>
</books>
<?xml version="1.0" encoding="UTF-8"?>
<books>
	<book>
		<name>Angels &amp; Demons</name>
		<isbn>9971-5-0210-0</isbn>
		<author>Dan Brown</author>
		<reviewcomment><![CDATA[This book is wonderful. Highly recommended.]]></reviewcomment>
	</book>
</books>
package com.xmlunit.tutorial.cdatatext;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.util.List;

import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.Difference;
import org.custommonkey.xmlunit.XMLUnit;
import org.xml.sax.SAXException;

public class CDataDiffTest {

	public static void main(String[] args) {
		URL url1 = CDataDiffTest.class.getResource("input1.xml");
		URL url2 = CDataDiffTest.class.getResource("input2.xml");
		FileReader fr1 = null;
		FileReader fr2 = null;
		try {
			fr1 = new FileReader(url1.getPath());
			fr2 = new FileReader(url2.getPath());
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		XMLUnit.setIgnoreDiffBetweenTextAndCDATA(Boolean.TRUE);

		try {
			Diff diff = new Diff(fr1, fr2);
			System.out.println("Similar? " + diff.similar());
			System.out.println("Identical? " + diff.identical());

			DetailedDiff detDiff = new DetailedDiff(diff);
			List differences = detDiff.getAllDifferences();
			for (Object object : differences) {
				Difference difference = (Difference)object;
				System.out.println("***********************");
				System.out.println(difference);
				System.out.println("***********************");
			}

		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

The output is

Similar? true
Identical? true

Refer line 29 for setting XMLUnit to ignore anamoly between the text based character content and CDATA based character content.

Consider the following xml documents input3.xml and input4.xml

input3.xml

<?xml version="1.0" encoding="UTF-8"?>
<persons>
	<person>
		<firstname>Steve</firstname>
	</person>
</persons>

input4.xml

<?xml version="1.0" encoding="UTF-8"?>
<persons>
	<person>
		<lastname>Gunn</lastname>
	</person>
</persons>

Note here that input3.xml has firstname tag which is missing in input4.xml and input4.xml has a lastname tag which is missing in input3.xml.

Here’s a typical test class:

package com.xmlunit.tutorial.unmatch;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.util.List;

import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.Difference;
import org.custommonkey.xmlunit.XMLUnit;
import org.xml.sax.SAXException;

public class UnMatchDiffTest {

	public static void main(String[] args) {
		URL url1 = UnMatchDiffTest.class.getResource("input3.xml");
		URL url2 = UnMatchDiffTest.class.getResource("input4.xml");
		FileReader fr1 = null;
		FileReader fr2 = null;
		try {
			fr1 = new FileReader(url1.getPath());
			fr2 = new FileReader(url2.getPath());
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		//XMLUnit.setCompareUnmatched(Boolean.FALSE);

		try {
			Diff diff = new Diff(fr1, fr2);
			System.out.println("Similar? " + diff.similar());
			System.out.println("Identical? " + diff.identical());

			DetailedDiff detDiff = new DetailedDiff(diff);
			List differences = detDiff.getAllDifferences();
			for (Object object : differences) {
				Difference difference = (Difference)object;
				System.out.println("***********************");
				System.out.println(difference);
				System.out.println("***********************");
			}

		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

The output generated is

Similar? false
Identical? false
***********************
Expected element tag name 'firstname' but was 'lastname' - comparing <firstname...> at /persons[1]/person[1]/firstname[1] to <lastname...> at /persons[1]/person[1]/lastname[1]
***********************
***********************
Expected text value 'Steve' but was 'Gunn' - comparing <firstname ...>Steve</firstname> at /persons[1]/person[1]/firstname[1]/text()[1] to <lastname ...>Gunn</lastname> at /persons[1]/person[1]/lastname[1]/text()[1]
***********************

Note that the firstname tag was compared with the lastname tag. This happens because as per the default setting an unmatched tag in the first xml is compared with the next available unmatched tag in the other xml. To change this behavior uncomment line 29.

The output generated is

Similar? false
Identical? false
***********************
Expected presence of child node 'firstname' but was 'null' - comparing <firstname...> at /persons[1]/person[1]/firstname[1] to  at null
***********************
***********************
Expected presence of child node 'null' but was 'lastname' - comparing  at null to <lastname...> at /persons[1]/person[1]/lastname[1]
***********************

This is an extremely useful feature which ensures an “apples to apples” comparison. The key thing to note here is the considerable amount of flexibility provided by the tool in providing a mature xml comparison technology. For an extensive documentation on the differences captured by XMLUnit please refer this url.

The most influencial aspect of XMLUnit is the logic it uses to compare two xml documents. Consider this, two xml documents are provided as input to the XMLUnit’s difference engine. The engine on the basis of some xml element/attribute matching logic makes a determination that node x in xml document 1 should be compared with node y in xml document 2. This logic is implemented by the concrete implementations of the ElementQualifier interface. All ElementQualifier child classes need to provide concrete implementation for the qualifyForComparison method. The method determines if two xml elements are suitable for comparison.

Let’s start with the first concrete implementation ElementNameQualifier. In this implementation, the comparison logic purely traverses the document hierarchy and tries to match the xml documents on the basis of the element tag names. The character content within the xml element is not considered. Consider the following xml documents. To a better insight into the inner workings of ElementNameQualifier, let’s consider the following xml documents.

reference.xml

<books>
	<book>
		<name>Angels &amp; Demons</name>
	</book>
	<book>
		<name>Harry Potter</name>
	</book>
</books>

sample1.xml

<books>
	<book>
		<name>Harry Potter</name>
	</book>
	<book>
		<name>Angels &amp; Demons</name>
	</book>
</books>

Structurally, both the xml documents are similar except for the fact that the Harry Potter book is in second position in reference.xml and first position in sample1.xml

To understand the behaviour of the ElementNameQualifier, let’s create a test class ElementQualifierDiffTest

package com.xmlunit.tutorial.match;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringWriter;
import java.net.URL;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.Difference;
import org.custommonkey.xmlunit.ElementNameQualifier;
import org.custommonkey.xmlunit.MatchTracker;
import org.custommonkey.xmlunit.NodeDetail;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

public class ElementQualifierDiffTest {

	public static void main(String[] args) {
		URL url1 = ElementQualifierDiffTest.class.getResource("reference.xml");
		URL url2 = ElementQualifierDiffTest.class.getResource("sample1.xml");
		FileReader fr1 = null;
		FileReader fr2 = null;
		try {
			fr1 = new FileReader(url1.getPath());
			fr2 = new FileReader(url2.getPath());
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		try {
			Diff diff = new Diff(fr1, fr2);
			System.out.println("Similar? " + diff.similar());
			System.out.println("Identical? " + diff.identical());

			DetailedDiff detDiff = new DetailedDiff(diff);
			detDiff.overrideMatchTracker(new MatchTrackerImpl());
			detDiff.overrideElementQualifier(new ElementNameQualifier());
			detDiff.getAllDifferences();

		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

class MatchTrackerImpl implements MatchTracker {

	public void matchFound(Difference difference) {
		if (difference != null) {
			NodeDetail controlNode = difference.getControlNodeDetail();
			NodeDetail testNode = difference.getTestNodeDetail();

			String controlNodeValue = printNode(controlNode.getNode());
			String testNodeValue = printNode(testNode.getNode());

			if (controlNodeValue != null) {
				System.out.println("####################");
				System.out.println("Control Node: " + controlNodeValue);
			}
			if (testNodeValue != null) {
				System.out.println("Test Node: " + testNodeValue);
				System.out.println("####################");
			}
		}
	}

	private static String printNode(Node node) {
		if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
			StringWriter sw = new StringWriter();
			try {
				Transformer t = TransformerFactory.newInstance().newTransformer();
				t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
				t.transform(new DOMSource(node), new StreamResult(sw));
			} catch (TransformerException te) {
				System.out.println("nodeToString Transformer Exception");
			}
			return sw.toString();

		}
		return null;
	}
}

Most of the code is similar to the earlier test clients except lines 45 and 46. We have provided custom implementatoins for MatchTracker and ElementQualifier. The MatchTracker implementation class MatchTrackerImpl’s matchFound method is invoked whenever XMLUnit’s difference processing engine determines two nodes to be a “match” for further processing. Using our custom MatchTracker implementation helps us in trapping this event and understanding what nodes within the xml documents are considered as a match. A custom printNode method implementation helps in providing a legible print of the node and its contents. Many thanks to Project Wow Now for providing the implementation. By overridding the ElementQualifier, the DetailedDiff is forced to use ElementNameQualifier class.

Here’s the output generated:

Similar? false
Identical? false
####################
Control Node: <books>
	<book>
		<name>Angels &amp; Demons</name>
	</book>
	<book>
		<name>Harry Potter</name>
	</book>	
</books>
Test Node: <books>
	<book>
		<name>Harry Potter</name>
	</book>	
	<book>
		<name>Angels &amp; Demons</name>
	</book>
</books>
####################
.
.
.
####################
Control Node: <book>
		<name>Angels &amp; Demons</name>
	</book>
Test Node: <book>
		<name>Harry Potter</name>
	</book>
####################
.
.
.
####################
Control Node: <book>
		<name>Angels &amp; Demons</name>
	</book>
Test Node: <book>
		<name>Harry Potter</name>
	</book>
####################
.
.
.
####################
Control Node: <name>Angels &amp; Demons</name>
Test Node: <name>Harry Potter</name>
####################
.
.
.
####################
Control Node: <book>
		<name>Harry Potter</name>
	</book>
Test Node: <book>
		<name>Angels &amp; Demons</name>
	</book>
####################
.
.
####################
Control Node: <name>Harry Potter</name>
Test Node: <name>Angels &amp; Demons</name>
####################

The entire output is too verbose, I have printed out the relevant sections. If you look at lines 4, 25 and 47. It is clear that comparisons have happened between the Harry Potter and Angel & Demons book and name elements. The ElementNameQualifier searches for the first relevant element match and processes the documents. For more tighter matching logic implementation, we will need to look at ElementNameAndAttributeQualifier and ElementNameAndTextQualifier.

To gain insight into the inner workings of ElementNameAndAttributeQualifier, consider the following xmls. reference.xml and sample.xml.

reference.xml

<books>
	<book publishyear="1998">
		<name>Harry Potter</name>
	</book>
	<book publishyear="1990">
		<name>Harry Potter</name>
	</book>	
</books>

sample.xml

<books>
	<book publishyear="1990">
		<name>Harry Potter</name>
	</book>	
	<book publishyear="1998">
		<name>Harry Potter</name>
	</book>
	<book>
		<name>You can win</name>
	</book>	
</books>

Using the ElementNameAndAttributeQualifier ensures that the matching logic is applied based on the book element and the publishyear attribute. The test class code is as below:

package com.xmlunit.tutorial.match.nameattr;

//imports removed for code legibility

public class ElementNameAttrDiffTest {

	public static void main(String[] args) {
		URL url1 = ElementNameAttrDiffTest.class.getResource("reference.xml");
		URL url2 = ElementNameAttrDiffTest.class.getResource("sample.xml");
		FileReader fr1 = null;
		FileReader fr2 = null;
		try {
			fr1 = new FileReader(url1.getPath());
			fr2 = new FileReader(url2.getPath());
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		try {
			Diff diff = new Diff(fr1, fr2);
			System.out.println("Similar? " + diff.similar());
			System.out.println("Identical? " + diff.identical());

			DetailedDiff detDiff = new DetailedDiff(diff);
			detDiff.overrideMatchTracker(new MatchTrackerImpl());
			detDiff.overrideElementQualifier(new ElementNameAndAttributeQualifier());

			List differences = detDiff.getAllDifferences();
			for (Object object : differences) {
				Difference difference = (Difference)object;
				System.out.println("***********************");
				System.out.println(difference);
				System.out.println("***********************");
			}


		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
}


class MatchTrackerImpl implements MatchTracker {

	//Implementation already available as a part of ElementQualifierDiffTest class code. 

}

The output generated is (only relevant snippets shown)

####################
Control Node: <books>
	<book publishyear="1998">
		<name>Harry Potter</name>
	</book>
	<book publishyear="1990">
		<name>Harry Potter</name>
	</book>	
</books>
Test Node: <books>
	<book publishyear="1990">
		<name>Harry Potter</name>
	</book>	
	<book publishyear="1998">
		<name>Harry Potter</name>
	</book>
	<book>
		<name>You can win</name>
	</book>	
</books>
####################
.
.
.
####################
Control Node: <book publishyear="1998">
		<name>Harry Potter</name>
	</book>
Test Node: <book publishyear="1998">
		<name>Harry Potter</name>
	</book>
####################
.
.
.
####################
Control Node: <name>Harry Potter</name>
Test Node: <name>Harry Potter</name>
####################
.
.
.
####################
Control Node: <book publishyear="1990">
		<name>Harry Potter</name>
	</book>
Test Node: <book publishyear="1990">
		<name>Harry Potter</name>
	</book>
####################
####################
Control Node: <name>Harry Potter</name>
Test Node: <name>Harry Potter</name>
####################

Please do take note of the fact that the element and attribute comparison is limited to the top level element below the root node of the xml document. In case the reference.xml and sample.xml had a format as shown below the Qualifier matching logic would have failed.

<books>
	<book>
		<name publishyear="1990">Harry Potter</name>
	</book>	
	<book>
		<name publishyear="1998">Harry Potter</name>
	</book>
	<book>
		<name>You can win</name>
	</book>	
</books>

It would not have been able to differentiate the name elements on the basis of publishyear attribute.

To understand the functioning of ElementNameAndTextQualifier implementation, consider the following two xmls reference and sample.

<books>
	<book publishyear="1998">Harry Potter</book>
	<book publishyear="1990">Angels &amp; Demons</book>	
</books>
<books>
	<book publishyear="1990">Angels &amp; Demons</book>	
	<book publishyear="1998">Harry Potter</book>
	<book>You can win</book>	
</books>

The test client code is exactly the same as ElementNameAttrDiffTest class. Only line 26 is changed to the following

detDiff.overrideElementQualifier(new ElementNameAndTextQualifier());

The output is (only relevant content is displayed)

####################
Control Node: <books>
	<book publishyear="1998">Harry Potter</book>
	<book publishyear="1990">Angels &amp; Demons</book>	
</books>
Test Node: <books>
	<book publishyear="1990">Angels &amp; Demons</book>	
	<book publishyear="1998">Harry Potter</book>
	<book>You can win</book>	
</books>
####################
.
.
.
####################
Control Node: <book publishyear="1998">Harry Potter</book>
Test Node: <book publishyear="1998">Harry Potter</book>
####################
.
.
.
####################
Control Node: <book publishyear="1990">Angels &amp; Demons</book>
Test Node: <book publishyear="1990">Angels &amp; Demons</book>
####################

If you noticed based on the text content the book elements have been matched. Let’s add a slight twist by changing the sample.xml to the following:

<books>
	<book publishyear="1998">Angels &amp; Demons</book>	
	<book publishyear="1990">Harry Potter</book>
	<book>You can win</book>	
</books>

I have interchanged the publishyear values. The output is as follows:

####################
Control Node: <books>
	<book publishyear="1998">Harry Potter</book>
	<book publishyear="1990">Angels &amp; Demons</book>	
</books>
Test Node: <books>
	<book publishyear="1998">Angels &amp; Demons</book>	
	<book publishyear="1990">Harry Potter</book>
	<book>You can win</book>	
</books>
####################
.
.
.
####################
Control Node: <book publishyear="1998">Harry Potter</book>
Test Node: <book publishyear="1990">Harry Potter</book>
####################
.
.
.
####################
Control Node: <book publishyear="1990">Angels &amp; Demons</book>
Test Node: <book publishyear="1998">Angels &amp; Demons</book>
####################

Note that in matching the publishyear attribute value was disregarded. Therefore we have two kinds of Qualifiers, one which matches purely on the basis of element and text and the other on element and attribute. However a combination of all the three aspects namely element, text and attribute is not supported; this is probably because comparison in most cases is expected between two aspects only. Again the comparison only works with top level element and text content below the document root. A structure like this does not work.

<books>
	<book publishyear="1998">
		<name>Harry Potter</name>
	</book>
	<book publishyear="1990">
		<name>Angels &amp; Demons</name>
	</book>	
</books>

OK what if I want to handle matching at a level deeper than just the top level. Well to achieve that XMLUnit provides two Qualifiers RecursiveElementNameAndTextQualifier and MultiLevelElementNameAndTextQualifier. However I have not been able to figure out how to use them. Maybe some other time.

About these ads
Categories: xml, xmlunit | Tags: , | 52 Comments

Post navigation

52 thoughts on “XML comparison tutorial using XMLUnit

  1. anon

    YOu might also want to investigate vtd-xml, which is a lot more powerful and advanced than DOM and SAX

  2. dave

    thanks for the tips, I’ve learnt a lot from it.
    can you post the detailed procedures of how to run the program in Eclipse?I am new in this and having a hard time from the very start.

    • Mr. President

      Refer url to help you get started on using Eclipse.

      • dave

        sorry i wasn’t being clear, I mean is there a way to apply XMLUnit in Eclipse?
        thank in advance.

      • Mr. President

        Dave,

        I am not sure if I correctly understand the question. For a Java project you just need to add the XMLUnit jars in the classpath. If you want to do xml files comparison using a Eclipse plugin type environment, I do not believe there is such a plugin.

  3. Vamshi Jampala

    Do you know how to convert the resulting DOM to a more interactive web page ? Any thoughts ?

    • Mr. President

      Could you please elaborate. I am not sure where you are getting DOM object from?

  4. Njko

    I need compare xml well formatted with the same not formatted
    like this
    xml1 = “\n\tXXX\n
    xml2=”XXX
    the answer of the test are
    similar? false
    identical? false

    is corect?
    There is a way tu use xmlunit for compare this xml?

    Thanks

    Njko

  5. KB

    Thanks for the information. it was real helpful.

    I have this situation. have two lists like this..

    list1:

    A Tale of Two Cities
    David Copperfield

    list2:

    David Copperfield
    A Tale of Two Cities

    set the compareIdentical to false.
    both lists have 2 book elements. but the order of the book elements is switched. how do I get the diff to return true since they are basically the same. I cannot control the 2nd file, because I get the result of the books from the dbase. and it doesnt always return in the same order. Please help. thanks.

  6. KB

    trying to make sure the xml elements show up.. hope this works..

    list1:

    A Tale of Two Cities
    David Copperfield

    list2:

    David Copperfield
    A Tale of Two Cities

  7. Mr. President

    As already explained in my post, Diff provides two methods for comparison identical and similar. Your requirement falls into similar. Identical expects content and order of elements to be same. Similar is less stricter and allows change in order. So in your case if order of elements is not important, you should use similar.

    • prasad

      Can u please provide code for this

      • Vinay Rajadhyaksha

        Unfortunately, the code in-lined in the blog post is all that I have.

  8. Hi,
    I want to compare two xml, but I don t want them to fail them if one xml is having some more attribute or less attribute.
    Is there any way to do this?

  9. Pingback: khemlall.info, Performance Testing Tip, Tricks, Scripting and more » How to Use xml unit to setup a xml comparison framework

  10. Pingback: JAVA / Правильная проверка XML данных в java-проектах « Зеркало IT индустрии

  11. This definitely makes perfect sense.

  12. i am trying to compare a large xml file [ around 40 MB ] . its taking more time [more than one hour.. is there any option to optimize the time ?

  13. Murali

    Thanks alot for the author. It made my job easier.

  14. Thanks a lot Author.. the info you provided in this post is very resourceful.. Thanks a ton…:)

  15. I have two xml files say abc.xml & 123.xml. The 123.xml contains all the contents of abc.xml and few extra content. so when i compare abc.xml to 123.xml using “XMLUNIT” i will get the output Similar – true (since the structure is same) and Identical – true(since the position is same). I wont be getting other output as the contents of abc.xml are present in 123.xml.
    Correct me If am wrong.

    • Mr. President

      Hi Sangramsanand,

      Both similar and identical will return false. You can override the behavior by implementing a custom ElementQualifier as done in this example.

    • Mr. President

      Noob,

      What you need to do is customize the difference engine behavior. Refer this example about implementing a custom DifferenceListener.

  16. vb999

    Thanks for the post, helped a lot. A couple of things I want to clarify:
    - Is there a verbose option I can use that gives me more information about the differences between 2 xml files. For example if I added a new attribute to one of the files (that wasnt present in the other). The detailed diff identifies that a new attribute was added, but doesnt show me the actual name of the book that was added. How can I do this (if possible)?

    - If I use this command:
    XMLUnit.setIgnoreAttributeOrder(true);
    does this do the same thing as a similar comparison (i.e. ignores the order of attributes). In this case then would similar() and identical() always return the same result?

    Thanks again for your help. Great tutorial.

    • Mr. President

      vb999,

      To get control over the difference display portion you can provide a custom implementation of the MatchTracker interface. I have provided a sample in my blog post, refer MatchTrackerImpl class.

      I have not used the setIgnoreAttributeOrder method so the following is purely my conjecture. Similar operation will consider both element and attribute order within an XML while doing a comparison. The setIgnoreAttributeOrder only specifies how to handle attribute order. So provided the elements are in the exact same order and the attribute ordering is different, by setting the ignoreattributeorder will ensure that similar and identical method return true.

  17. Andrew

    I am looking on how to compare 2 xml this is great but my issues is how can i compare the two xml via stream. the xmls are very big files and idk the virtual memory i am allocated. is there a way to check compare by stream?

  18. Pingback: How to compare 2 large XML documents in Java

  19. Nicky

    I am getting NullPointerException while retrieving xml file like URL url2 = ComparisonTest.class.getResource(“/WEB-INF/comparison.xml”); please helpme out to ressolve this.

    • Mr. President

      Hi Nicky,

      The getResource method picks up files located in the classpath. WEB-INF folder does not form part of the classpath. The comparison.xml should be located in the WEB-INF/classes folder. That should do the trick.

  20. Kinnu

    Thanks for pretty nice examples and usage. I need to compare 2 XML files of same object type say Class A which has some differences. Now after comparing the 2 XMLs i have to report what are the fields that are differing and what is the value in each XML that is different. How do i do that using XMLunit ?

    Your timely response wis appreciated.

    Thanks
    Kinnu

    • Mr. President

      Hi Kinnu,

      You will need to implement a custom DifferenceListener. For a ready reference look at the implementation in this post. The Listener provides a handle to the Difference object. The Difference object maintains two NodeDetail references; these point to the control and test node. The NodeDetail provides methods to access the node values.

      Hope this helps.

  21. Chris Walcott

    Greetings,

    This tutorial has been really helpful and has gotten me close to where I need to be but there are a couple of things that i’m missing.

    I have 2 XML results (from an api call) that are identical with the exception of 2 to 5 nodes that will be added to one string. I need to compare the 2 results and confirm that they are identical except for the extra nodes. I also need to know what the nodes are to make sure they are the ones that I expect them to be.

    I used the first code example on this page. The print out to the console has 3 lines starting with “Expected presence of child node ‘null’ but was ‘the extra node’ comparing at null to at …..

    This is good. I can see in the output the nodes that are added to the 2nd XML result.

    My problem is how to parse this. I tried doing this:
    if(difference.contains(“myNode”) )
    and get an error that “the method contains(String) is undefined for type Difference.” Right. Difference is not a String and can’t be subjected to String methods.

    How do I take action on the contents of difference?

    Is there any way to cast the difference line that you print out with System.out.println() as a String?

    Are there any example code out there that you know of that would address my issue?

    Thanks!

  22. Sul

    I have a question regarding “Similar” and “Identical”. Your example above :

    Dan Brown

    AND

    Dan Brown

    Output:

    Similar? true
    Identical? false
    ***********************
    Expected sequence of child nodes ’5′ but was ’7′ – comparing at /books[1]/book[1]/author[1] to at /books[1]/book[1]/author[1]
    ***********************
    ***********************
    Expected sequence of child nodes ’7′ but was ’5′ – comparing at /books[1]/book[1]/category[1] to at /books[1]/book[1]/category[1]
    ***********************

    shows this behaviour. My question how can we “NOT” include in the output such non-identical information? My problem is that I have a long xml documents to compare. Now there are cases, where the differences are related to orderring and in addition to others.

    But when i get the total output, it gets kind of difficult to find the actual differences (non-orderring) related.

    I have to manually skip the non-important information (orderring differences) from the actual differences for example if anything is missing in the reference documents or have different content etc.

    So it would be very nice and quick to get to the actual differences in the output which are not actually differences related to the orderring.

    Rest the api works perfectly well!

    Good job.

  23. Sul

    I have a question regarding “Similar” and “Identical”. Your example above :

    Dan Brown

    AND

    Dan Brown

    Output:

    Similar? true
    Identical? false
    ***********************
    Expected sequence of child nodes ’5′ but was ’7′ – comparing at /books[1]/book[1]/author[1] to at /books[1]/book[1]/author[1]
    ***********************
    ***********************
    Expected sequence of child nodes ’7′ but was ’5′ – comparing at /books[1]/book[1]/category[1] to at /books[1]/book[1]/category[1]
    ***********************

    shows this behaviour. My question how can we “NOT” include in the output such non-identical information? My problem is that I have a long xml documents to compare. Now there are cases, where the differences are related to orderring and in addition to others.

    But when i get the total output, it gets kind of difficult to find the actual differences (non-orderring) related.

    I have to manually skip the non-important information (orderring differences) from the actual differences for example if anything is missing in the reference documents or have different content etc.

    So it would be very nice and quick to get to the actual differences in the output which are not actually differences related to the orderring.

    Rest the api works perfectly well!

    Good job.

    • Sul

      Any update about my last enquiry Mr. President?

      • Mr. President

        Hi Sul,

        The only way to control the Difference behavior is to develop a custom DifferenceListener. For ready sample refer here.

      • Sul

        I am now doing the following…

        XMLUnit.setIgnoreComments(Boolean.TRUE);
        XMLUnit.setIgnoreWhitespace(Boolean.TRUE);
        XMLUnit.setNormalizeWhitespace(Boolean.TRUE);
        XMLUnit.setIgnoreDiffBetweenTextAndCDATA(Boolean.TRUE);
        XMLUnit.setIgnoreAttributeOrder(Boolean.TRUE);

        XMLUnit.setCompareUnmatched(Boolean.FALSE);

        try {
        Diff diff = new Diff(fr1, fr2);
        System.out.println(” Similar ? ” + diff.similar());
        System.out.println(” Identical ? ” + diff.identical());

        diff.overrideDifferenceListener(new DifferenceListener() {

        @Override
        public void skippedComparison(Node arg0, Node arg1) {
        // TODO Auto-generated method stub

        }

        @Override
        public int differenceFound(Difference difference) {
        if (difference.getId() == DifferenceConstants.CHILD_NODELIST_SEQUENCE_ID)
        {
        return RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
        }

        return RETURN_ACCEPT_DIFFERENCE;
        }

        });

        ..

        but it does not seem to work fine:

        ref.xml

        EXCURSION_AND_TICKETS

        TRAVELGUIDE

        com.xml

        TRAVELGUIDE

        EXCURSION_AND_TICKETS

        output:

        Similar ? false
        Identical ? false
        ***********************
        Expected presence of child node ‘m:OtherSrvcName’ but was ‘null’ – comparing at /Envelope[1]/Header[1]/Security[1]/UsernameToken[1]/OtherSrvcPref[1]/OtherSrvcName[1] to at null
        ***********************
        ***********************
        Expected presence of child node ‘null’ but was ‘m:OtherSrvcName’ – comparing at null to at /Envelope[1]/Header[1]/Security[1]/UsernameToken[1]/OtherSrvcPref[1]/OtherSrvcName[1]
        ***********************
        ***********************
        Expected presence of child node ‘m:OtherSrvcName’ but was ‘null’ – comparing at /Envelope[1]/Header[1]/Security[1]/UsernameToken[1]/OtherSrvcPref[2]/OtherSrvcName[1] to at null
        ***********************
        ***********************
        Expected presence of child node ‘null’ but was ‘m:OtherSrvcName’ – comparing at null to at /Envelope[1]/Header[1]/Security[1]/UsernameToken[1]/OtherSrvcPref[2]/OtherSrvcName[1]
        ***********************

        the above example works if i change the XMLs to:

        m:UsernameToken>
        EXCURSION_AND_TICKETS
        TRAVELGUIDE

        and

        TRAVELGUIDE
        EXCURSION_AND_TICKETS

        respectively.

        Coudl you please help in identifying the problem?

        Thanks.

      • Sul

        Any update Mr. President? For better readability you can check here: https://sourceforge.net/projects/xmlunit/forums/forum/73274/topic/6698456

        Thanks.

  24. Sul

    Any update Mr. President? For better readability you can check here: https://sourceforge.net/projects/xmlunit/forums/forum/73274/topic/6698456

    Thanks.

    • Mr. President

      Hi Sul,
      Can you try RecursiveElementNameAndTextQualifier or MultiLevelElementNameAndTextQualifier?

      • Sul

        I replaced:

        //detDiff.overrideElementQualifier(new ElementNameAndTextQualifier());
        

        with:

        detDiff.overrideElementQualifier(new RecursiveElementNameAndTextQualifier());

        the previous examples worked, but on my changing the example slightly more, it failed:

        reference.xml

        <?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>
        <SOAP-ENV:Envelope xmlns:SOAP-ENV=\\\"http://schemas.xmlsoap.org/soap/envelope/\\\">
        	<SOAP-ENV:Header>
        		<m:Security xmlns:m=\\\"http://schemas.xmlsoap.org/ws/2002/12/secext\\\">
        			<m:BinarySecurityToken/>
        		</m:Security>
        	</SOAP-ENV:Header>
        	<SOAP-ENV:Body>
        		<ns3:OTA_TT_ProfileReadRS Target=\\\"Test\\\" Version=\\\"1.0\\\" xmlns:ns3=\\\"http://www.opentravel.org/OTA/2003/05\\\">
        			<ns3:Success/>
        			<ns3:Profiles>
        				<ns3:ProfileInfo>
        					<ns3:UniqueID ID=\\\"B_100001_be_user\\\" ID_Context=\\\"login\\\" Type=\\\"99\\\" URL=\\\"abc.def.gef\\\"/>
        					<ns3:Profile ProfileType=\\\"CUS\\\">
        						<ns3:Customer CurrencyCode=\\\"EUR\\\">
        							<ns3:PersonName>
        								<ns3:NamePrefix>Mr</ns3:NamePrefix>
        								<ns3:Surname>TT_DE_TestUser</ns3:Surname>
        							</ns3:PersonName>
        						</ns3:Customer>
        						<ns3:UserID ID=\\\"100001\\\" ID_Context=\\\"KID\\\" Type=\\\"99\\\"/>
        						<ns3:PrefCollections>
        							<ns3:PrefCollection>
        								<ns3:CommonPref>
        									<ns3:InsurancePref PreferLevel=\\\"Preferred\\\">MONDIAL-FR</ns3:InsurancePref>
        									<ns3:InsurancePref PreferLevel=\\\"Unacceptable\\\">ELVIA</ns3:InsurancePref>
        								</ns3:CommonPref>
        								<ns3:VehicleRentalPref>
        									<ns3:VendorPref Code=\\\"SIXT\\\" PreferLevel=\\\"Preferred\\\"/>
        									<ns3:VendorPref Code=\\\"SUNC\\\" PreferLevel=\\\"Preferred\\\"/>
        								</ns3:VehicleRentalPref>
        								<ns3:AirlinePref/>
        								<ns3:TransferSrvcPref>
        									<ns3:TransferSrvcName>AIRPORT_HOTEL_TRANSPORT</ns3:TransferSrvcName>
        									<ns3:VendorPref Code=\\\"A2B\\\" PreferLevel=\\\"Preferred\\\"/>
        									<ns3:VendorPref Code=\\\"HTX\\\" PreferLevel=\\\"Preferred\\\"/>
        								</ns3:TransferSrvcPref>
        								<ns3:OtherSrvcPref>
        									<ns3:OtherSrvcName>TRAVELGUIDE</ns3:OtherSrvcName>
        									<ns3:VendorPref Code=\\\"XYZ\\\" PreferLevel=\\\"Preferred\\\"/>
        									<ns3:VendorPref Code=\\\"MMV\\\" PreferLevel=\\\"Preferred\\\"/>
        								</ns3:OtherSrvcPref>
        								<ns3:OtherSrvcPref>
        									<ns3:OtherSrvcName>EXCURSION_AND_TICKETS</ns3:OtherSrvcName>
        									<ns3:VendorPref Code=\\\"XYZ\\\" PreferLevel=\\\"Preferred\\\"/>
        									<ns3:VendorPref Code=\\\"GYG\\\" PreferLevel=\\\"Preferred\\\"/>
        								</ns3:OtherSrvcPref>
        								<ns3:FrontendPref>
        									<ns3:PrintPreferences>
        										<ns3:Document Id=\\\"statusOffer\\\" IncludeHotelInformation=\\\"false\\\" MaxImageNumber=\\\"0\\\"/>
        										<ns3:Document Id=\\\"statusOption\\\" IncludeHotelInformation=\\\"false\\\" MaxImageNumber=\\\"0\\\"/>
        										<ns3:Document Id=\\\"statusCancel\\\" IncludeHotelInformation=\\\"false\\\" MaxImageNumber=\\\"0\\\"/>
        										<ns3:Document Id=\\\"statusBook\\\" IncludeHotelInformation=\\\"false\\\" MaxImageNumber=\\\"0\\\"/>
        									</ns3:PrintPreferences>
        								</ns3:FrontendPref>
        							</ns3:PrefCollection>
        						</ns3:PrefCollections>
        					</ns3:Profile>
        				</ns3:ProfileInfo>
        			</ns3:Profiles>
        		</ns3:OTA_TT_ProfileReadRS>
        	</SOAP-ENV:Body>
        </SOAP-ENV:Envelope>
        

        comparison.xml

        <?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>
        <SOAP-ENV:Envelope xmlns:SOAP-ENV=\\\"http://schemas.xmlsoap.org/soap/envelope/\\\">
        	<SOAP-ENV:Header>
        		<m:Security xmlns:m=\\\"http://schemas.xmlsoap.org/ws/2002/12/secext\\\">
        			<m:BinarySecurityToken/>
        		</m:Security>
        	</SOAP-ENV:Header>
        	<SOAP-ENV:Body>
        		<ns3:OTA_TT_ProfileReadRS Target=\\\"Test\\\" Version=\\\"1.0\\\" xmlns:ns3=\\\"http://www.opentravel.org/OTA/2003/05\\\">
        			<ns3:Success/>
        			<ns3:Profiles>
        				<ns3:ProfileInfo>
        					<ns3:UniqueID ID=\\\"B_100001_be_user\\\" ID_Context=\\\"login\\\" Type=\\\"99\\\" URL=\\\"abc.def.gef\\\"/>
        					<ns3:Profile ProfileType=\\\"CUS\\\">
        						<ns3:Customer CurrencyCode=\\\"EUR\\\">
        							<ns3:PersonName>
        								<ns3:NamePrefix>Mr</ns3:NamePrefix>
        								<ns3:Surname>TT_DE_TestUser</ns3:Surname>
        							</ns3:PersonName>
        						</ns3:Customer>
        						<ns3:UserID ID=\\\"100001\\\" ID_Context=\\\"KID\\\" Type=\\\"99\\\"/>
        						<ns3:PrefCollections>
        							<ns3:PrefCollection>
        								<ns3:CommonPref>
        									<ns3:InsurancePref PreferLevel=\\\"Preferred\\\">MONDIAL-FR</ns3:InsurancePref>
        									<ns3:InsurancePref PreferLevel=\\\"Unacceptable\\\">ELVIA</ns3:InsurancePref>
        								</ns3:CommonPref>
        								<ns3:VehicleRentalPref>
        									<ns3:VendorPref Code=\\\"SIXT\\\" PreferLevel=\\\"Preferred\\\"/>
        									<ns3:VendorPref Code=\\\"SUNC\\\" PreferLevel=\\\"Preferred\\\"/>
        								</ns3:VehicleRentalPref>
        								<ns3:AirlinePref/>
        								<ns3:OtherSrvcPref>
        									<ns3:OtherSrvcName>TRAVELGUIDE</ns3:OtherSrvcName>
        									<ns3:VendorPref Code=\\\"XYZ\\\" PreferLevel=\\\"Preferred\\\"/>
        									<ns3:VendorPref Code=\\\"MMV\\\" PreferLevel=\\\"Preferred\\\"/>
        								</ns3:OtherSrvcPref>
        								<ns3:TransferSrvcPref>
        									<ns3:TransferSrvcName>AIRPORT_HOTEL_TRANSPORT</ns3:TransferSrvcName>
        									<ns3:VendorPref Code=\\\"A2B\\\" PreferLevel=\\\"Preferred\\\"/>
        									<ns3:VendorPref Code=\\\"HTX\\\" PreferLevel=\\\"Preferred\\\"/>
        								</ns3:TransferSrvcPref>
        								<ns3:OtherSrvcPref>
        									<ns3:OtherSrvcName>EXCURSION_AND_TICKETS</ns3:OtherSrvcName>
        									<ns3:VendorPref Code=\\\"XYZ\\\" PreferLevel=\\\"Preferred\\\"/>
        									<ns3:VendorPref Code=\\\"GYG\\\" PreferLevel=\\\"Preferred\\\"/>
        								</ns3:OtherSrvcPref>
        								<ns3:FrontendPref>
        									<ns3:PrintPreferences>
        										<ns3:Document Id=\\\"statusOffer\\\" IncludeHotelInformation=\\\"false\\\" MaxImageNumber=\\\"0\\\"/>
        										<ns3:Document Id=\\\"statusOption\\\" IncludeHotelInformation=\\\"false\\\" MaxImageNumber=\\\"0\\\"/>
        										<ns3:Document Id=\\\"statusCancel\\\" IncludeHotelInformation=\\\"false\\\" MaxImageNumber=\\\"0\\\"/>
        										<ns3:Document Id=\\\"statusBook\\\" IncludeHotelInformation=\\\"false\\\" MaxImageNumber=\\\"0\\\"/>
        									</ns3:PrintPreferences>
        								</ns3:FrontendPref>
        							</ns3:PrefCollection>
        						</ns3:PrefCollections>
        					</ns3:Profile>
        				</ns3:ProfileInfo>
        			</ns3:Profiles>
        		</ns3:OTA_TT_ProfileReadRS>
        	</SOAP-ENV:Body>
        </SOAP-ENV:Envelope>
        

        the difference above is changed positions: between ns3:TransferSrvcPref and ns3:OtherSrvcPref.

        Output:

        [quote] Similar ? false
        Identical ? false
        ***********************
        Expected presence of child node \\\’SOAP-ENV:Body\\\’ but was \\\’null\\\’ – comparing at /Envelope[1]/Body[1] to at null
        ***********************
        ***********************
        Expected presence of child node \\\’null\\\’ but was \\\’SOAP-ENV:Body\\\’ – comparing at null to at /Envelope[1]/Body[1]
        ***********************
        [/quote]

        which is quite strange :(.

        I also tried to use MultiLevelElementNameAndTextQualifier with different level argumen, but it didnt change anything.

        Any help?

  25. Mr. President

    Hi Sul,

    Sorry but I am out of ideas.

  26. Pingback: StasyakOFF Blog » Blog Archive » Пример работы с XMLUnit (вольный перевод)

  27. Raksha

    Hi,

    I am comparing 2(file1 and file2) similar XML files on the basis of their values change.
    I want to customize the default output coming from DetailedDiff object.
    The output would be in following manner.

    “: is updated”

    Can anybody please help.
    Thanks in advance.

    Cheers
    Raksha

    • Raksha

      Hi,

      I am comparing 2(file1 and file2) similar XML files on the basis of their values change.
      I want to customize the default output coming from DetailedDiff object.
      The output would be in following manner.

      “Name of the tag getting updated in file2:(updated value) is updated”

      Can anybody please help.
      Thanks in advance.

  28. vicky

    Hi All, Please reply, if we have any idea. How we can compare two XMLs by skipping some of their elements? Please suggest, I am in need of this example.

  29. Jona

    is there a zip file available for the reference, comparison and java file with the other bits. I just finished high school and am very interested in building webservices and at the moment I am trying to compare 2 xmls, extract the attribute and value and compare them and set True if it matches and false if it doesnt and spit out the difference in a nice pretty looking report. (well thats what I am planning to do and trying to work with)

    Anyway are you able to put up a link so anyone can download the whole tutorial. This is a really interesting post.

    • Vinay Rajadhyaksha

      Sorry, Jona. The post is old and I do not have the source code. The source code listed in the post is all that I have!

  30. naveen

    I have two xmls as stated below instead of 4 differences , 3 pm elements missed out and one ml missed out. I am seeding many differences. I used Recursiveelementand test qualifier

    hindi
    english
    spanish
    french
    dutch
    portugal

    height
    155cms

    weight
    23

    size
    155cms

    count
    2773

    holidaylist
    12

    second sample xml:

    xyz

    hindi
    english
    spanish
    french
    dutch
    portugal

    height
    155cms

    weight
    23

  31. Priyanka Gour

    Can any one help me ?
    Getting out of this problem.

    reference.xml

    TRAVELGUIDE

    EXCURSION_AND_TICKETS

    sample.xml

    EXCURSION_AND_TICKETS

    TRAVELGUIDE

    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    import java.net.URL;
    import java.util.List;

    import org.custommonkey.xmlunit.DetailedDiff;
    import org.custommonkey.xmlunit.Diff;
    import org.custommonkey.xmlunit.Difference;
    import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier;
    import org.custommonkey.xmlunit.ElementNameAndTextQualifier;
    import org.custommonkey.xmlunit.ElementNameQualifier;
    import org.custommonkey.xmlunit.XMLUnit;
    import org.custommonkey.xmlunit.examples.MultiLevelElementNameAndTextQualifier;
    import org.custommonkey.xmlunit.examples.RecursiveElementNameAndTextQualifier;
    import org.xml.sax.SAXException;

    public class ElementNameAttrDiffTest {

    public static void main(String[] args) {
    URL url1 = ElementNameAttrDiffTest.class.getResource(“reference.xml”);
    URL url2 = ElementNameAttrDiffTest.class.getResource(“sample.xml”);
    FileReader fr1 = null;
    FileReader fr2 = null;
    try {
    fr1 = new FileReader(url1.getPath());
    fr2 = new FileReader(url2.getPath());
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    }
    XMLUnit.setIgnoreWhitespace(Boolean.TRUE);
    XMLUnit.setIgnoreAttributeOrder(true);
    try {
    Diff diff = new Diff(fr1, fr2);
    System.out.println(“Similar? ” + diff.similar());
    System.out.println(“Identical? ” + diff.identical());

    DetailedDiff detDiff = new DetailedDiff(diff);
    detDiff.overrideMatchTracker(new MatchTrackerImpl());
    //detDiff.overrideElementQualifier(new ElementNameQualifier());
    // detDiff.overrideElementQualifier(new ElementNameAndAttributeQualifier());
    detDiff.overrideElementQualifier(new MultiLevelElementNameAndTextQualifier(2));
    List differences = detDiff.getAllDifferences();
    for (Object object : differences) {
    Difference difference = (Difference)object;
    System.out.println(“***********************”);
    System.out.println(difference);
    System.out.println(“***********************”);
    }

    } catch (SAXException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }

    }

    import java.io.StringWriter;

    import javax.xml.transform.OutputKeys;
    import javax.xml.transform.Transformer;
    import javax.xml.transform.TransformerException;
    import javax.xml.transform.TransformerFactory;
    import javax.xml.transform.dom.DOMSource;
    import javax.xml.transform.stream.StreamResult;

    import org.custommonkey.xmlunit.Difference;
    import org.custommonkey.xmlunit.MatchTracker;
    import org.custommonkey.xmlunit.NodeDetail;
    import org.w3c.dom.Node;

    class MatchTrackerImpl implements MatchTracker {

    public void matchFound(Difference difference) {
    if (difference != null) {
    NodeDetail controlNode = difference.getControlNodeDetail();
    NodeDetail testNode = difference.getTestNodeDetail();

    String controlNodeValue = printNode(controlNode.getNode());
    String testNodeValue = printNode(testNode.getNode());

    if (controlNodeValue != null) {
    System.out.println(“####################”);
    System.out.println(“Control Node: ” + controlNodeValue);
    }
    if (testNodeValue != null) {
    System.out.println(“Test Node: ” + testNodeValue);
    System.out.println(“####################”);
    }
    }
    }

    private static String printNode(Node node) {
    if (node != null && node.getNodeType() == Node.ELEMENT_NODE) {
    StringWriter sw = new StringWriter();
    try {
    Transformer t = TransformerFactory.newInstance().newTransformer();
    t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, “yes”);
    t.transform(new DOMSource(node), new StreamResult(sw));
    } catch (TransformerException te) {
    System.out.println(“nodeToString Transformer Exception”);
    }
    return sw.toString();

    }
    return null;
    }

    }

    Similar? false
    Identical? false
    ####################

    Expected sequence of child nodes ’0′ but was ’1′ – comparing at /UsernameToken[1]/OtherSrvcPref[1] to at /UsernameToken[1]/OtherSrvcPref[2]
    ***********************
    ***********************
    Expected sequence of child nodes ’1′ but was ’0′ – comparing at /UsernameToken[1]/OtherSrvcPref[2] to at /UsernameToken[1]/OtherSrvcPref[1]

    above is the xml’s and the code which i am using.

    How i will overcome/ignore the sequence of Nodes.?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com. The Adventure Journal Theme.

Follow

Get every new post delivered to your Inbox.

Join 56 other followers

%d bloggers like this: