I have an xml document (using the Document class in the XMLParser library of GWT client) with a format like follows:
<document><node id="0">content</node><node id="1">more content</node></document>
Given an ID, I need to insert a new node immediately after the node with that ID.
So far I've tried using insertBefore (as there is no insertAfter), but I must be using it incorrectly as nothing happens (apart from an UmbrellaException in the js console). I can't find any example usage via search engines.
My attempt is as follows (where n is the node I want to insert after):
Node nNext = n.getNextSibling(); //To get the next sibling to use it with insertBefore
Element newNode = doc.createElement("node");
newNode.appendChild(doc.createTextNode("new content")); //seems to work up until here
n.insertBefore(newNode, nNext); //so this line could be the problem?
insertBefore must be called on the parent node, so:
n.getParentNode().insertBefore(newNode, n.getNextSibling());
Related
Can anyone please explain the difference between the Element object and Node object provided in JSoup ?
Which is the best thing to be used in which situation/condition.
A node is the generic name for any type of object in the DOM hierarchy.
An element is one specific type of node.
The JSoup class model reflects this:
Node
Element
Since Element extends Node anything you can do on a Node, you can do on an Element too. But Element provides additional behaviour which makes it easier to use, for example; an Element has properties such as id and class etc which make it easier to find them in a HTML document.
In most cases using Element (or one of the other subclasses of Document) will meet your needs and will be easier to code to. I suspect the only scenario in which you might need to fall back to Node is if there is a specific node type in the DOM for which JSoup does not provide a subclass of Node.
Here's an example showing the same HTML document inspection using both Node and Element:
String html = "<html><head><title>This is the head</title></head><body><p>This is the body</p></body></html>";
Document doc = Jsoup.parse(html);
Node root = doc.root();
// some content assertions, using Node
assertThat(root.childNodes().size(), is(1));
assertThat(root.childNode(0).childNodes().size(), is(2));
assertThat(root.childNode(0).childNode(0), instanceOf(Element.class));
assertThat(((Element) root.childNode(0).childNode(0)).text(), is("This is the head"));
assertThat(root.childNode(0).childNode(1), instanceOf(Element.class));
assertThat(((Element) root.childNode(0).childNode(1)).text(), is("This is the body"));
// the same content assertions, using Element
Elements head = doc.getElementsByTag("head");
assertThat(head.size(), is(1));
assertThat(head.first().text(), is("This is the head"));
Elements body = doc.getElementsByTag("body");
assertThat(body.size(), is(1));
assertThat(body.first().text(), is("This is the body"));
YMMV but I think the Element form is easier to use and much less error prone.
It's seem like same. but different.
Node have Element. and additionally have TextNode too.
so... Example.
<p>A<span>B</span></p>
In P Elements.
.childNodes() // get node list
-> A
-> <span>B</span>
.children() // get element list
-> <span>B</span>
<xml-fragment>
<currentClinicalNote>
<patientFamilyHistory disorderName="CurrentCN" id="23423"/>
<patientFamilyHistory disorderName="CurrentCN1" id="23424"/>
<patientFamilyHistory disorderName="CurrentCN1" id="23424"/>
</currentClinicalNote>
</xml-fragment>
I have an XMLBean like above, now I want to replace the node[#id=23423] with a new same type node. How can I do that?
Below is sample code I tried to work..
XmlCursor xmlCursor = cursor.execQuery(nameSpace + pathExpression1);
I found the node with above code, now I have that node in cursor, How do I replace that with another?
Any replies would be appreciated.
DOM3 is not yet implemented in XML Beans,so need to work with DOM Nodes directly to fetch by finding first child in cursor.
I have the following code:
DocumentBuilder dBuilder = dbFactory_.newDocumentBuilder();
StringReader reader = new StringReader(s);
InputSource inputSource = new InputSource(reader);
Document doc_ = dBuilder.parse(inputSource);
and then I would like to create a new element in that node right under the root node with this code:
Node node = doc_.createElement("New_Node");
node.setNodeValue("New_Node_value");
doc_.getDocumentElement().appendChild(node);
The problem is that the node gets created and appended but the value isn't set. I don't know if I just can't see the value when I look at my xml if its hidden in some way but I don't think that's the case because I've tried to get the node value after the create node call and it returns null.
I'm new to xml and dom and I don't know where the value of the new node is stored. Is it like an attribute?
<New_Node value="New_Node_value" />
or does it put value here:
<New_Node> New_Node_value </New_Node>
Any help would be greatly appreciated,
Thanks, Josh
The following code:
Element node = doc_.createElement("New_Node");
node.setTextContent("This is the content"); //adds content
node.setAttribute("attrib", "attrib_value"); //adds an attribute
produces:
<New_Node attrib="attrib_value">This is the content</New_Node>
Hope this clarifies.
For clarification, when you create nodes use:
Attr x = doc.createAttribute(...);
Comment x = doc.createComment(...);
Element x = doc.createElement(...); // as #dogbane pointed out
Text x = doc.createTextNode(...);
instead of using the generic Node for what you get back from each method. It will make your code easier to read/debug.
Secondly, the getNodeValue() / setNodeValue() methods work differently depending on what type of Node you have. See the summary of the Node class for reference. For an Element, you can't use these methods, although for a Text node you can.
As #dogbane pointed out, use setTextContent() for the text between this element's tags. Note that this will destroy any existing child elements.
This is other solution, in my case this solution is working because the setTextContent() function not exist. I am working with Google Web Toolkit (GWT) (It is a development framework Java) and I am imported the XMLParser library for I can use DOM Parser.
import com.google.gwt.xml.client.XMLParser;
Document doc = XMLParser.createDocument();
Element node = doc.createElement("New_Node");
node.appendChild(doc.createTextNode("value"));
doc.appendChild(node);
The result is:
<New_Node> value </New_Node>
<New_Node value="New_Node_value" />
'value' is an attribute of
New_Node
element, for getting into DOM I suggest you http://www.w3schools.com/htmldom/default.asp
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.
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));
}
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.
I'm having trouble with copying nodes from one document to another one. I've used both the adoptNode and importNode methods from Node but they don't work. I've also tried appendChild but that throws an exception. I'm using Xerces. Is this not implemented there? Is there another way to do this?
List<Node> nodesToCopy = ...;
Document newDoc = ...;
for(Node n : nodesToCopy) {
// this doesn't work
newDoc.adoptChild(n);
// neither does this
//newDoc.importNode(n, true);
}
The problem is that Node's contain a lot of internal state about their context, which includes their parentage and the document by which they are owned. Neither adoptChild() nor importNode() place the new node anywhere in the destination document, which is why your code is failing.
Since you want to copy the node and not move it from one document to another there are three distinct steps you need to take...
Create the copy
Import the copied node into the destination document
Place the copied into it's correct position in the new document
for(Node n : nodesToCopy) {
// Create a duplicate node
Node newNode = n.cloneNode(true);
// Transfer ownership of the new node into the destination document
newDoc.adoptNode(newNode);
// Make the new node an actual item in the target document
newDoc.getDocumentElement().appendChild(newNode);
}
The Java Document API allows you to combine the first two operations using importNode().
for(Node n : nodesToCopy) {
// Create a duplicate node and transfer ownership of the
// new node into the destination document
Node newNode = newDoc.importNode(n, true);
// Make the new node an actual item in the target document
newDoc.getDocumentElement().appendChild(newNode);
}
The true parameter on cloneNode() and importNode() specifies whether you want a deep copy, meaning to copy the node and all it's children. Since 99% of the time you want to copy an entire subtree, you almost always want this to be true.
adoptChild does not create a duplicate, it just moves the node to another parent.
You probably want the cloneNode() method.