views:

1496

answers:

2

I have two XML docs that I've created and I want to combine these two inside of a new envelope. So I have

<alert-set>
  <warning>National Weather Service...</warning>
  <start-date>5/19/2009</start-date>
  <end-date>5/19/2009</end-date>
</alert-set>

and

 <weather-set>
   <chance-of-rain type="percent">31</chance-of-rain>
   <conditions>Partly Cloudy</conditions>
   <temperature type="Fahrenheit">78</temperature>
 </weather-set>

What I'd like to do is combine the two inside a root node: < DataSet> combined docs < /DataSet>

I've tried creating a temporary doc and replacing children with the root nodes of the documents:

<DataSet>
  <blank/>
  <blank/>
</DataSet>

And I was hoping to replace the two blanks with the root elements of the two documents but I get "WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it." I tried adopting and importing the root nodes but I get the same error.

Is there not some easy way of combining documents without having to read through and create new elements for each node?

EDIT: Sample code snippets Just trying to move one to the "blank" document for now... The importNode and adoptNode functions cannot import/adopt Document nodes, but they can't import the element node and its subtree... or if it does, it does not seem to work for appending/replacing still.

 Document xmlDoc;     //created elsewhere
 Document weather = getWeather(latitude, longitude);
 Element weatherRoot = weather.getDocumentElement();

 Node root = xmlDoc.getDocumentElement();
 Node adopt = weather.adoptNode(weatherRoot);
 Node imported = weather.importNode(weatherRoot, true);
 Node child = root.getFirstChild();

 root.replaceChild(adopt, child);      //initially tried replacing the <blank/> elements
 root.replaceChild(imported, child);

 root.appendChild(adopt);
 root.appendChild(imported);
 root.appendChild(adopt.cloneNode(true));

All of these throw the DOMException: WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it.

I think I'll have to figure out how to use stax or just reread the documents and create new elements... That kinda seems like too much work just to combine documents, though.

+2  A: 

It's a bit tricky, but the following example runs:

public static void main(String[] args) {

 DocumentImpl doc1 = new DocumentImpl();
 Element root1 = doc1.createElement("root1");
 Element node1 = doc1.createElement("node1");
 doc1.appendChild(root1);
 root1.appendChild(node1);

 DocumentImpl doc2 = new DocumentImpl();
 Element root2 = doc2.createElement("root2");
 Element node2 = doc2.createElement("node2");
 doc2.appendChild(root2);
 root2.appendChild(node2);

 DocumentImpl doc3 = new DocumentImpl();
 Element root3 = doc3.createElement("root3");
 doc3.appendChild(root3);

 // root3.appendChild(root1); // Doesn't work -> DOMException
 root3.appendChild(doc3.importNode(root1, true));

 // root3.appendChild(root2); // Doesn't work -> DOMException
 root3.appendChild(doc3.importNode(root2, true));   
}
Andreas_D
Thanks, that works. I was importing the root node the its own document instead of the document I was trying to combine everything to. Sometimes taking a lunch break solves everything.Thanks.
ravun
+1  A: 

I know you got the issue solved already, but I still wanted to take a stab at this problem using the XOM library that I'm currently testing out (related to this question), and while doing that, offer a different approach than that of Andreas_D's answer.

(To simplify this example, I put your <alert-set> and <weather-set> into separate files, which I read into nu.xom.Document instances.)

import nu.xom.*;

[...]

Builder builder = new Builder();
Document alertDoc = builder.build(new File("src/xomtest", "alertset.xml"));
Document weatherDoc = builder.build(new File("src/xomtest", "weatherset.xml"));
Document mainDoc = builder.build("<DataSet><blank/><blank/></DataSet>", "");

Element root = mainDoc.getRootElement();
root.replaceChild(
    root.getFirstChildElement("blank"), alertDoc.getRootElement().copy());
root.replaceChild(
    root.getFirstChildElement("blank"), weatherDoc.getRootElement().copy());

The key is to make a copy of the elements to be inserted into mainDoc; otherwise you'll get a complain that "child already has a parent".

Outputting mainDoc now gives:

<?xml version="1.0" encoding="UTF-8"?>
<DataSet>
    <alert-set>
        <warning>National Weather Service...</warning>
        <start-date>5/19/2009</start-date>
        <end-date>5/19/2009</end-date>
    </alert-set>
    <weather-set>
        <chance-of-rain type="percent">31</chance-of-rain>
        <conditions>Partly Cloudy</conditions>
        <temperature type="Fahrenheit">78</temperature>
    </weather-set>
</DataSet>

To my delight, this turned out to be very straight-forward to do with XOM. It only took a few minutes to write this, even though I'm definitely not very experienced with the library yet. (It would have been even easier without the <blank/> elements, i.e., starting with simply <DataSet></DataSet>.)

So, unless you have compelling reasons for using only the standard JDK tools, I warmly recommend trying out XOM as it can make XML handling in Java much more pleasant.

Jonik
Thanks a lot. I did end up doing it without the <blank/> elements since they were pointless... I was just trying to wrap my head around DOM. Building documents with XOM definitely looks easier than what I was doing. I'm an intern so I'm just getting my feet wet, but looking back I can already tell most of the code I wrote only a couple weeks ago is pretty ugly. I've since learned how to use XSLT and javax.xml.Transform and that has made things a lot easier.Thanks again for your reply and link to XOM. I am sure I'll be using it soon.
ravun
Glad to be of help :) Without <blank/> elements, you'd just do root.appendChild(alertDoc.getRootElement().copy()) and so on. By the way, take a look at http://www.xom.nu/apidocs/nu/xom/xslt/XSLTransform.html to see if XOM might help you with XSLT too.
Jonik