How to combine two XML in Java

I am trying to combine two xmls in Java. I use the STAX API to write these XML. I searched a lot on the internet about how to combine xmls, but no one looks as straight forward as C # . Is there a direct way to do this in Java using StAX? Xslt is probably not the right solution, as the file size may be large.

File1.xml

<TestCaseBlock> <TestCase TestCaseID="1"> <Step ExecutionTime="2011-03-29 12:08:31 EST"> <Status>Passed</Status> <Description>foo</Description> <Expected>foo should pass</Expected> <Actual>foo passed</Actual> </Step> </TestCase> </TestCaseBlock> 

File2.xml

 <TestCaseBlock> <TestCase TestCaseID="2"> <Step ExecutionTime="2011-03-29 12:08:32 EST"> <Status>Failed</Status> <Description>test something</Description> <Expected>something expected</Expected> <Actual>not as expected</Actual> </Step> </TestCase> </TestCaseBlock> 

Merged.xml

 <TestCaseBlock> <TestCase TestCaseID="1"> <Step ExecutionTime="2011-03-29 12:08:33 EST"> <Status>Passed</Status> <Description>foo</Description> <Expected>foo should pass</Expected> <Actual>foo passed</Actual> </Step> </TestCase> <TestCase TestCaseID="2"> <Step ExecutionTime="2011-03-29 12:08:34 EST"> <Status>Failed</Status> <Description>test something</Description> <Expected>something expected</Expected> <Actual>not as expected</Actual> </Step> </TestCase> </TestCaseBlock> 
+6
source share
6 answers

I have a solution that works for me. Now experts, please advise if this is the way to go.

Thanks, -Nilesh

  XMLEventWriter eventWriter; XMLEventFactory eventFactory; XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); XMLInputFactory inputFactory = XMLInputFactory.newInstance(); eventWriter = outputFactory.createXMLEventWriter(new FileOutputStream("testMerge1.xml")); eventFactory = XMLEventFactory.newInstance(); XMLEvent newLine = eventFactory.createDTD("\n"); // Create and write Start Tag StartDocument startDocument = eventFactory.createStartDocument(); eventWriter.add(startDocument); eventWriter.add(newLine); StartElement configStartElement = eventFactory.createStartElement("","","TestCaseBlock"); eventWriter.add(configStartElement); eventWriter.add(newLine); String[] filenames = new String[]{"test1.xml", "test2.xml","test3.xml"}; for(String filename:filenames){ XMLEventReader test = inputFactory.createXMLEventReader(filename, new FileInputStream(filename)); while(test.hasNext()){ XMLEvent event= test.nextEvent(); //avoiding start(<?xml version="1.0"?>) and end of the documents; if (event.getEventType()!= XMLEvent.START_DOCUMENT && event.getEventType() != XMLEvent.END_DOCUMENT) eventWriter.add(event); eventWriter.add(newLine); test.close(); } eventWriter.add(eventFactory.createEndElement("", "", "TestCaseBlock")); eventWriter.add(newLine); eventWriter.add(eventFactory.createEndDocument()); eventWriter.close(); 
+3
source

The general solution will still be XSLT, but you need to merge two files into one large XML first with a wrapper element (XSLT works with a single input source).

 <root> <TestCaseBlock> <TestCase TestCaseID="1"> ... </TestCase> </TestCaseBlock> <TestCaseBlock> <TestCase TestCaseID="2"> ... </TestCase> </TestCaseBlock> </root> 

Then just do the XSLT to match = "// TestCase" and unload all the test cases, ignoring which block of the test block they belong to.

And don't worry about performance until you try. JAva's XML API is much better than in 2003.

This is the stylesheet you need:

 <?xml version="1.0" encoding="ISO-8859-1"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" encoding="UTF-8" indent="yes"/> <xsl:template match="/"> <TestCaseBlock> <xsl:apply-templates/> </TestCaseBlock> </xsl:template> <xsl:template match="//TestCase"> <xsl:copy-of select="."/> </xsl:template> </xsl:stylesheet> 

Tested, it works.

BTW, this XSLT was compiled and executed on this (small) example in 1 ms.

+2
source

If the structure is fairly regular, so you can use data binding, I would actually consider binding XML from both files to objects using JAXB, then merging objects, serializing in XML format. If the file sizes are large, you can also just snap subtrees; for this you use XMLStreamReader (from Stax api, javax.xml.stream) to iterate to the element that is the root, attach this element (and its children) to the desired object, move on to the next root element.

+1
source

I think XSLT and SAX might be the solution.

If you work with Stream that STaX is a solution, I read the Sun tutorial, I think it is very useful: Sun Tutorail on STaX

Bye

0
source

Check out XmlCombiner , which is a Java library that implements XML merging in this way. It is freely based on similar functionality offered by the plexus-utils library.

In your case, the tags should also be mapped based on the value of the TestCaseID attribute. Here is a complete example:

 import org.atteo.xmlcombiner.XmlCombiner; // create combiner XmlCombiner combiner = new XmlCombiner("TestCaseID"); // combine files combiner.combine(firstFile); combiner.combine(secondFile); // store the result combiner.buildDocument(resultFile); 

Disclaimer: I am the author of the library.

0
source

You can consider XML as a text file and combine them. It is very fast compared to other methods. Please look at the code below: -

 import java.io.*; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; public class XmlComb { static Set<String> lstheader = new HashSet<String>(); public static void main(String[] args) throws IOException { Map<String,List<String>> map1 = getMapXml("J:\\Users\\Documents\\XMLCombiner01\\src\\main\\resources\\File1.xml"); Map<String,List<String>> map2 = getMapXml("J:\\Users\\Documents\\XMLCombiner01\\src\\main\\resources\\File2.xml"); Map<String,List<String>> mapCombined = combineXML(map1, map2); lstheader.forEach( lst -> { System.out.println(lst); }); try { mapCombined.forEach((k,v) -> { System.out.println(k); v.forEach(val -> System.out.println(val)); }); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static Map<String,List<String>> combineXML(Map<String, List<String>> map1, Map<String, List<String>> map2 ) { Map<String,List<String>> map2Modified = new TreeMap<String, List<String>>(); Map<String,List<String>> mapCombined = new TreeMap<String, List<String>>(); // --- Modifying map --- for(String strKey2 : map2.keySet()) { if(map1.containsKey(strKey2)) { map2Modified.put(strKey2.split("\">")[0] + "_1\">", map2.get(strKey2)); } else { map2Modified.put(strKey2 , map2.get(strKey2)); } } //---- Combining map --- map1.putAll(map2Modified); return map1; } public static Map<String,List<String>> getMapXml(String strFilePath) throws IOException{ File file = new File(strFilePath); BufferedReader br = new BufferedReader(new FileReader(file)); Map<String, List<String>> testMap = new TreeMap<String, List<String>>(); List<String> lst = null; String st; String strCatalogName = null; while ((st = br.readLine()) != null) { //System.out.println(st); if(st.toString().contains("<TestCase")){ lst = new ArrayList<String>(); strCatalogName = st; testMap.put(strCatalogName, lst); } else if(st.contains("</TestCase")){ lst.add(st); testMap.put(strCatalogName,lst); } else { if(lst != null){ lst.add(st); }else { lstheader.add(st); } } } return testMap; } } 
0
source

Source: https://habr.com/ru/post/886017/


All Articles