简体   繁体   中英

Merging multiple XML files in Java

I've been looking for the best way to do this, but I can't seem to find a clear answer how this should be done.

I have an Arraylist of Files in my Java code, representing a list of xml files which should be merged and written to a new XML file. This is not a fixed length list, I'm estimating it would be between 2-10 files. All these files have a very similar document structure, but some attributes should be summed while merging. For example:

File1

<events>
  <commandEvents date="2013-07-16">
    <commandEvent count="1" commandId="update"/>
    <commandEvent count="1" commandId="debug"/>
    <commandEvent count="3" commandId="resume"/>
  </commandEvents>
</events>

File 2

<events>
  <commandEvents date="2013-07-16">
    <commandEvent count="2" commandId="resume"/>
  </commandEvents>
  <commandEvents date="2013-07-15">
    <commandEvent count="2" commandId="resume"/>
    <commandEvent count="1" commandId="update"/>
  </commandEvents>
</events>

Result

<events>
  <commandEvents date="2013-07-16">
    <commandEvent count="1" commandId="update"/>
    <commandEvent count="1" commandId="debug"/>
    <commandEvent count="5" commandId="resume"/>
  </commandEvents>
  <commandEvents date="2013-07-15">
    <commandEvent count="2" commandId="resume"/>
    <commandEvent count="1" commandId="update"/>
  </commandEvents>
</events>

To clarify, the merging should occur on commandEvents[@date]/commandEvent[@commandId].The commandEvent elements have some more attributes, but these are the same for each element so I've omitted them here. Not all dates will be available in each document.

I first found some answers to go the XSLT route, but I'm quite confused about the XSLT syntax to do this. Although I'm not entirely sure about the size that these files may reach, but I would be highly suprised they would be >1mb, so a Java DOM parser as JDOM or XOM might work as well, but I'd have to load all these files at the same time or iterate in pairs.

What is regarded as the best way to do this? And if XSLT is regarded as the best solution, would it be possible to give me some tips for this?

Here's a simple merge, in which all children of the root node in one document get appended to the root node of a second document:

public static void mergeSecondLevel(Document from, Document to) {
    Element fromRoot = from.getDocumentElement();
    Element toRoot = to.getDocumentElement();

    Node child = null;
    while ((child = fromRoot.getFirstChild()) != null) {
        to.adoptNode(child);
        toRoot.appendChild(child);
    }
}

If you're trying to do some sort of processing on the nodes before merging them (you say some attributes should be summed), then this won't be sufficient. There's a linked post that covers using XPath to retrieve nodes, but even then you're going to have to write logic to ensure the correct updates.

Check XmlCombiner which is a Java library that implements XML merging and allows to add the filter in which you can specify the logic for summing the values of the 'count' attribute.

Here is the code for the initialization of the library:

import org.atteo.xmlcombiner.XmlCombiner;

// create combiner specifying the attributes which are used as a keys
XmlCombiner combiner = new XmlCombiner(Lists.newArrayList("date", "commandId"));
// add the filter
combiner.setFilter(filter);
// combine files
combiner.combine(firstFile);
combiner.combine(secondFile);
// store the result
combiner.buildDocument(resultFile);

And here is the code for the filter itself:

XmlCombiner.Filter filter = new XmlCombiner.Filter() {
    @Override
    public void postProcess(Element recessive, Element dominant, Element result) {
        if (recessive == null || dominant == null) {
            return;
        }
        Attr recessiveNode = recessive.getAttributeNode("count");
        Attr dominantNode = dominant.getAttributeNode("count");
        if (recessiveNode == null || dominantNode == null) {
            return;
        }

        int recessiveValue = Integer.parseInt(recessiveNode.getValue());
        int dominantValue = Integer.parseInt(dominantNode.getValue());

        result.setAttribute("count", Integer.toString(recessiveValue + dominantValue));
    }
};

Disclaimer: I am the author of the XmlCombiner.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM