Get child nodes by searching for parent node using XPath - java

My XML structure is as under
<Employee>
<categoryType>Name</categoryType>
<groupNames>
<name>ABC</name>
<name>XYZ</name>
<name>PQR</name>
</groupNames>
</Employee>
I am trying to get hold of the child nodes by searching for parent node (categoryType = Name ).
Tried using all the following combinations , but none of them return the child nodes.
//*[contains(#categoryType,'Name')]/groupNames
Employee/*(#categoryType,'Name')/groupNames
Any suggestions would be appreciated.

You are matching an attribute categoryType which doesn't exist. You have to match an element. Replace #categoryType for categoryType and you will have a different result.
This:
//*[contains(categoryType,'Name')]/groupNames
will get the categoryType node. And this:
//*[contains(categoryType,'Name')]/groupNames/name
will return a node-set with all three names.

Related

How to get the element from the xml using Xpath Java

<employees>
<employee>
<firstName>Lokesh</firstName>
<lastName>Gupta</lastName>
<department>
<id>101</id>
<name>IT</name>
</department>
</employee>
</employees>
I wanted to get the elements name using Xpath..
I need to count the number of elements that i am getting using count(//employees/*) and count(//employees/employee/department/*)
it is returning count of each parent..
I need to get the element names as well //employees/employee/*/name() to get the elements name FirstName, LastName and Department..
also (//employees/employee/department/*/name()) to return name and id.. but it is showing error javax.xml.transform.TransformerException: Unknown nodetype: name .
You want to get the elements names (not the value of it). name() has to appear the first.
Since javax only supports XPath 1.0, you can use :
concat(name(//employees/employee/*[1]),",",name(//employees/employee/*[2]),",",name(//employees/employee/*[3]))
Output : firstName,lastName,department
concat(name(//employees/employee/department/*[1]),",",name(//employees/employee/department/*[2]))
Output : id,name
If you don't know the number of child for each parent element, you should use a loop approach. First, count and store the number of child (count(//employees/employee/*)), then make a loop where you increase the position index ([i]) at each iteration //employees/employee/*[i] i=i+1.

How to get only first level nodes with Jsoup

I have following XML tree and I'm using Jsoup to parse it.
<?xml version="1.0" encoding="UTF-8" ?>
<nodes>
<node>
<name>NODE 1</name>
<value1>
<value1>NODE 1 VALUE 1</value1>
</value1>
<nodes>
<node>
<name>NODE 1 CHILD</name>
<value1>NODE 1 CHILD VALUE 1</value1>
</node>
</nodes>
</node>
<node>
<name>NODE 2</name>
<value1>NODE 2 VALUE 1</value1>
</node>
</nodes>
However when I try to get only first level of node-elements. It returns all elements including children nodes, and it is doing it correctly, because clearly child elements also match my query.
Elements elements = data.select("nodes > node");
Is there any way to get just first level node-elements without adding additional level information to XML data?
You can do something like this:
Elements elements = data.select("nodes").first().select("> node");
This will work as well:
Elements elements = data.select("> nodes > node");
but only if you've used Jsoup.parse(xml, "", Parser.xmlParser()) to parse the XML and the XML is indeed as you've specified in your question (<nodes> is the root element)

Add XML element in a child node if parent node meets a condition

I have a very simple question for you experts of XML.
I want to add a new Element beside tuple_centre_name, with the same tag and different value, only if the username have a value I want, and another tuple_centre_name with the same value, here amministrazione, does NOT exist.
I ask you because I find difficult to reach that element, check if another tuple_centre_name exists and then check the parent attribute.
I'm using DOM in JAVA.
Thanks for helping me.
<?xml version="1.0" encoding="UTF-8" standalone="no"?><accounts>
<account>
<username>fabio</username>
<password>123456</password>
<node>
<tuple_centre_name>amministrazione</tuple_centre_name>
<port>NP</port>
</node>
</account>
Example is shown here. Request: add a new tuple_centre_name named something else only if it does not already exist inside the element named fabio. Here, is the result I want:
<?xml version="1.0" encoding="UTF-8" standalone="no"?><accounts>
<account>
<username>fabio</username>
<password>123456</password>
<node>
<tuple_centre_name>amministrazione</tuple_centre_name>
<tuple_centre_name>Something else</tuple_centre_name> //ok because `fabio` as username is fine for me
<port>NP</port>
</node>
</account>
You want XPath for this:
Document document = /* ... */;
final String username = "fabio";
XPathVariableResolver resolver = new XPathVariableResolver() {
#Override
public Object resolveVariable(QName varName) {
return varName.getLocalPart().equals("user") ? username : null;
}
};
Element newElement = document.createElement("tuple_centre_name");
newElement.appendChild(document.createTextNode("amministraione"));
XPath xpath = XPathFactory.newInstance().newXPath();
xpath.setXPathVariableResolver(resolver);
Node tupleCentreNameNode = (Node) xpath.evaluate(
"//account[username[text()=$user]]/node/tuple_centre_name",
document,
XPathConstants.NODE);
tupleCentreNameNode.getParentNode().insertBefore(newElement,
tupleCentreNameNode.getNextSibling());
XPath usually looks like a directory path, which is where the account/node/tuple_centre_name part comes from. The two slashes at the start (//account) means "any account element, any number of levels deep in the document."
The account[username[text()=$user]] selector means "an account element which contains a username element whose text matches the XPath variable 'user'." XPath variables can be defined externally with an XPathVariableResolver. While it is possible to hard-code the username value in the XPath expression, you would need to escape various characters, including quotes and characters which are special in XML; passing the username as an XPath variable guarantees it will be correct.

how to add an attribute to an XML element

I am using the DOM parser. I have to parse the following XML:
<abc>
<type action="">
<code>test</code>
<value>001</value>
</type>
<type action="">
<code>test2</code>
<value>002</value>
</type>
</abc>
so, depending on the value field under the type field, I have to fill in the action attribute in the type field. I am a bit stumped. I am able to get the value of the value field, but I don't know how to go back and add the attribute.
Any help will be appreciated a lot!!!
thanks!
To go back, just save a reference to the type Element before you traverse to its value child. (assuming you visited it already).
to change the value, use the setAttribute() method.
edit:
Alternate method: from the value text node, call getParentNode() twice (once to get back to the value element & once to get back to the type element), then call setAttribute() after you do any necissary casting.
try something like
nodelist = doc.getElementsByTagName("value");
for (Element element : nodelist) {
Element parent = element.getParentNode()
parent.setAttribute("action", "attrValue");
}

Query Node in XOM

I am querying document in XOM, getting a Node, and then querying this Node for another Node. However, querying Node behaves like it's querying whole document, not only this node.
XML is like this:
<root>
<someotherstuff>
<DifferentNode>
<Value1>different-value1</Value1>
</DifferentNode>
<Node>
<Node>
<Value1>value1</Value1>
<Value2>value2</Value2>
</Node>
<Node>
<Value1>value3</Value1>
<Value2>value4</Value2>
</Node>
<!-- more Node's -->
</Node>
</someotherstuff>
</root>
And I'm doing this:
Nodes nodes = document.query("//Node/Node", X_PATH_CONTEXT);
Node node = nodes.get(0);
Nodes innerNodes = node.query("/Value1");
And innerNodes contains 0 children. When I change "/Value1" to "//Value1" (slash added) then I'm getting different-value1, so it looks like it's querying whole document, instead of my selected Node.
How can I query specific Node in XOM?
When your query starts with a single slash then this looks for the document root node with the given name. Two slashes in contrast means to look for all ancestors of the given name. Your query should work if you simply omit the leading slash:
Nodes innerNodes = node.query("Value1");

Categories

Resources