Remove Element from JDOM document using removeContent() - java

Given the following scenario, where the xml, Geography.xml looks like -
<Geography xmlns:ns="some valid namespace">
<Country>
<Region>
<State>
<City>
<Name></Name>
<Population></Population>
</City>
</State>
</Region>
</Country>
</Geography>
and the following sample java code -
InputStream is = new FileInputStream("C:\\Geography.xml");
SAXBuilder saxBuilder = new SAXBuilder();
Document doc = saxBuilder.build(is);
XPath xpath = XPath.newInstance("/*/Country/Region/State/City");
Element el = (Element) xpath.selectSingleNode(doc);
boolean b = doc.removeContent(el);
The removeContent() method doesn't remove the Element City from the content list of the doc. The value of b is false
I don't understand why is it not removing the Element, I even tried to delete the Name & Population elements from the xml just to see if that was the issue but apparently its not.
Another way I tried, I don't know why I know its not essentially different, still just for the sake, was to use Parent -
Parent p = el.getParent();
boolean s = p.removeContent(new Element("City"));
What might the problem? and a possible solution? and if anyone can share the real behaviour of the method removeContent(), I suspect it has to do with the parent-child relationship.

Sure, removeContent(Content child) removes child if child belongs to the parents immediate children, which it does not in your case. Use el.detach()instead.

If you want to remove the City element, get its parent and call removeContent:
XPath xpath = XPath.newInstance("/*/Country/Region/State/City");
Element el = (Element) xpath.selectSingleNode(doc);
el.getParent().removeContent(el);
The reason why doc.removeContent(el) does not work is because el is not a child of doc.
Check the javadocs for details. There are a number of overloaded removeContent methods there.

This way works keeping in mind that .getParent() returns a Parent object instead of an Element object, and the detach() method which eliminates the actual node, must be called from an Element.
Instead do:
el.getParentElement().detach();
This will remove the parent element with all it's children !

Related

How to know the element contain namespace(xmlns) or not?

<xxx1 xmlns="hello">
<xxx2>
<xxx3>
<name>rule_1</name>
</xxx3>
</xxx2>
</xxx1>
I select node by "//*[namespace-uri()='hello']/*[local-name()='name']"
It should get //hello:xxx1/xxx2/xxx3/name , and it does.
Now I try to get element . In reality, I don't know how much parent for <name> will get <xxx1>;
I try this code
node.getParent().getNamespaceURI() = "Hello"
and increase getParent() amount to get <xxx1>
But the first time I call <xxx3>.getNamespaceURI() it returns true.
Is the namespace inherited?
How to get the element has or not has xmlns?
Sorry for my question was not clearly.
I'm trying to get the element which is the first declared namespace "hello".
<xxx1 xmlns="hello">
<xxx2>
<xxx3>
this three node which one is contained xmlns="hello", 'cause <xxx2> and <xxx3> was not declare xmlns in the label.
Hello and Welcome to Stack Overflow!
Yes, namespaces are sort of inherited, but the terminology normally used is that, in your example, the <name> element is in the scope of the namespace declaration xmlns="hello", so the <name>element will be in the hello namespace.
With DOM4J, you can test whether an element is in a namespace or not like this:
boolean hasNamespace(Element e) {
return e.getNamespaceURI().length() > 0;
}
If the element is not in any namespace, getNamespaceURI() returns an empty string.
I guess that you want to select the <name> element, but you don't know at which level it be, i.e. how many parents it will have. You can always use this XPath expression:
Node node = doc.selectSingleNode("//*[namespace-uri() = 'foo' and local-name() = 'name']");

Difference between JSoup Element and JSoup Node

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>

How do I create an xPath statement from a NodeInfo?

I'm using the S9API with Saxon 9.7 HE, and I have a NodeInfo object. I need to create an xPath statement that uniquely corresponds to this NodeInfo. It's easy enough to get the prefix, the node name, and the parent:
String prefix = node.getPrefix();
String localPart = node.getLocalPart();
NodeInfo parent = node.getParent();
So I can walk up the tree, building the xPath as I go. But what I can't find is any way to get the positional predicate info. IOW, it's not sufficient to create:
/persons/person/child
because it might match multiple child elements. I need to create:
/persons/person[2]/child[1]
which will match only one child element. Any ideas on how to get the positional predicate info? Or maybe there's a better way to do this altogether?
BTW, for those who use the generic DOM and not the S9API, here's an easy solution to this problem: http://practicalxml.sourceforge.net/apidocs/net/sf/practicalxml/DomUtil.html#getAbsolutePath(org.w3c.dom.Element)
Edit: #Michael Kay's answer works. To add some meat to it:
XPathExpression xPathExpression = xPath.compile("./path()");
List matches = (List) xPathExpression.evaluate(node, XPathConstants.NODESET);
String pathToNode = matches.get(0).toString();
// If you want to remove the expanded QName syntax:
pathToNode = pathToNode.replaceAll("Q\\{.*?\\}", "");
This must be done using the same xPath object that was previously used to acquire the NodeInfo object.
In XPath 3.0 you can use fn:path().
Earlier Saxon releases offer saxon:path().
The challenge here is handling namespaces. fn:path() returns a path that's not sensitive to namespace-prefix bindings by using the new expanded-QName syntax
/Q{}persons/Q{}person[2]/Q{}child[1]

How to get elements from XPath in Java

I want to get data from an XPath query:
Element location = (Element) doc.query("//location[location_name='"+ locationName +"']/*").get(0).getDocument().getRootElement();
System.out.println(location.toXML());
Element loc = location.getFirstChildElement("location");
System.out.println(loc.getFirstChildElement("location_name").getValue());
However, no matter what I choose, I always get 1 node (because of .get(0)). I don't know how to select the node which was selected by query.
I found that I should cast the node to Element, (XOM getting attribute from Node?) but the link only shows how to select the first node.
Call getParent() on the first element in the result:
Builder parse = new Builder();
Document xml = parse.build("/var/www/JAVA/toForum.xml");
System.out.println(xml.query("//location[#id=83]/*").get(0).getParent().toXML());
Produces the following output:
<location id="83">
<location_name>name</location_name>
<company_name>company a</company_name>
<machines>
<machine id="12">A</machine>
<machine id="312">B</machine>
</machines>
</location>
The call you make to getDocument() is returning the entirety of the XML document.
The call to query() returns a Nodes object directly containing references to the nodes that you are after.
If you change to
Element location = (Element)doc.query(
"//location[location_name='"+ locationName +"']/*").get(0);
System.out.println(location.getAttribute("location_name").getValue());
it should be ok
EDIT (by extraneon)
Some extra explanation not worthy of an answer by itself:
By doing
Element location =
(Element) doc.query("//location[location_name='"
+ locationName +"']/*").get(0)
.getDocument().getRootElement();
you search through the tree and get the requested node. But then you call getDocument().getRootNode() on the element you want, which will give you the uppermost node of the document.
The above query can thus be simplified to:
Element location = (Element)doc.getRootElement();
which is not wahat you intended.
It's a bit like a bungie jump. You go down to where you need to be (the element) but go immediately back to where you came from (the root element).
It's not clear (at least for me) what actually has to be done. From your query you should get list of nodes matching the given criteria. You will get NodeList and then you can iterate over this NodeList and get content of each node with getNodeValue for example.

Problems setting a new node value in java, dom, xml parsing

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

Categories

Resources