Get XML child node from parent node using XPATH java - java

I'm trying to get specific child node from list of nodes using xpath.
Here is my xml input
<root>
<Transaction>
<code> 123 </code>
<Reason> test1 </Reason>
</Transaction>
<Transaction>
<code> 456 </code>
</Transaction>
<Transaction>
<code> 789 </code>
<Reason> test2 </Reason>
</Transaction>
</root>
I'm trying to get all the transactions as node list and then check one by one inside each trancation either it has reason or not all using Xpath. Here is my sample code.
Document document = builder.parse(new FileInputStream(file));
XPath xPath = XPathFactory.newInstance().newXPath();
//Get all the transactions
NodeList nodeList = (NodeList) xPath.evaluate("//Transaction", document, XPathConstants.NODESET);
for (int temp = 0; temp < nodeList.getLength(); temp++) {
Node node = nodeList.item(temp);
Element element = (Element) node;
if (node.getNodeType() == Node.ELEMENT_NODE) {
XPath xPath2 = XPathFactory.newInstance().newXPath();
//This one always return first value
Node child = (Node) xPath2.evaluate("//Reason", node, XPathConstants.NODE);
if(child != null) {
System.out.println(child.getTextContent());
}
//This is working as expected
if(element.getElementsByTagName("Reason").getLength() > 0) {
System.out.println(element.getElementsByTagName("Reason").item(0).getTextContent());
}
}
}
If I cast the node to element and try to get child element by tag name it's working fine. But when I try to do it using X-PAth it returns all the values from other nodes as well.

There's no need to execute the loop in Java; you can do it in the XPath expression itself:
//Transaction[Reason]
That XPath will return you all the Transaction elements which have a child Reason element.
If you want to get the Reason elements which are children of a Transaction element, then use this XPath:
//Transaction/Reason
If you want to get all the text nodes which are children of Reason elements which are children of a Transaction element, then use this XPath:
//Transaction/Reason/text()

Related

How to update empty Node value using Xpath java?

I am trying to update empty node value using xpath.When Node has some value set,its getting updated .but if node value is empty,its not getting updated.
Below is code snippet:
Document document = dBuilder.parse(new File(filename));
document.getDocumentElement().normalize();
XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "/Document/FIToFICstmrCdtTrf/GrpHdr/TtlIntrBkSttlmAmt";
NodeList nodes = (NodeList) xPath.evaluate(expression, document,
XPathConstants.NODESET);
nodes.item(0).setNodeValue("ABC");
This is the right way:
node.appendChild(document.createTextNode("ABC"));
It would be more efficient to fetch a node only instead of the nodelist (if this is the only node):
Node node = (Node) xPath.evaluate(expression, document, XPathConstants.NODE);
You can check the existing value in this ways:
1, if you use your current xpath, you have to check that is there child node or not:
node.getFirstChild() != null // in this case, the node.getFirstChild().getNodeValue() gives back the stored text.
2, or, you can test it in this way:
String expression = "/Document/FIToFICstmrCdtTrf/GrpHdr/TtlIntrBkSttlmAmt/text()";
Node node = (Node) xPath.evaluate(expression, document,
XPathConstants.NODE);
node != null // in this case, the node.getNodeValue() gives back the stored text.

Xpath insert sequence of nodes under a node - Java

I am trying to add sequence of nodes under a node, below is my reference xml:
<?xml version="1.0" encoding="UTF-8"?>
<transformation>
<info>
<name>Bulkload</name>
<description/>
<extended_description/>
<trans_version/>
<trans_type>Normal</trans_type>
<trans_status>0</trans_status>
<directory>/</directory>
</info>
<connection>
<name>con_name</name>
<server>server</server>
<type>SYBASE</type>
<access>Native</access>
<database>database</database>
<port>port</port>
<username>user</username>
<password>Encrypted xyz</password>
</connection>
<step>
<name>Extract</name>
<type>TextFileOutput</type>
<fields>
**HERE**
<field>
</field>
</fields>
<cluster_schema/>
<remotesteps> <input> </input> <output> </output> </remotesteps> <GUI>
<xloc>300</xloc>
<yloc>168</yloc>
<draw>Y</draw>
</GUI>
</step>
</transformation>
I want to add below xml block inside field tag:
<field>
<name>field_name</name>
<type>Integer</type>
<format>#;-#</format>
<currency/>
<decimal>.</decimal>
<group>,</group>
<nullif/>
<trim_type>none</trim_type>
<length>9</length>
<precision>0</precision>
</field>
My requirement is to create new xml documents from the above template. I only found a method in Xpath to insert node before a node so I updated my template and added a blank field node in it inside fields node and with that I was able to insert a node with the below code:
File dest =new File("H:\\Project_Documents\\reference.ktr");
DocumentBuilderFactory factory = DocumentBuilderFactory
.newInstance();
factory.setIgnoringComments(true);
DocumentBuilder builder = factory.newDocumentBuilder();
String newLine = System.getProperty("line.separator");
Document document = builder.parse(dest);
XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();
NodeList nodes = document.getElementsByTagName("field");
Text a = document.createTextNode("Anup");
Element p =document.createElement("field");
p.appendChild(a);
nodes.item(0).getParentNode().insertBefore(p, nodes.item(0));
but again I faced the dame problem for child nodes i.e name, type, format, etc. Could anybody please show me a way to achieve my purpose?
If I cannot achieve it with Xpath then which parser I should use?
One solution, which works:
You have to manage two XML Documents, find the tag you want, and replace.
With XPath you can select more precisely, but no use there.
1 get a DOM for your global XML
Document document= ...
2 get the node you want to insert
Document document_to_insert = builder.parse(new InputSource(new StringReader(xml_to_insert))); // ...
//GET NODE field
NodeList nodes_field_to_insert=document_to_insert.getElementsByTagName("field");
Element node_field_to_insert=null;
for(int i=0; i<nodes_field_to_insert.getLength(); i++)
{
Node the_node = nodes_field_to_insert.item(i);
// WE TAKE THE FIRST ONE
if(the_node instanceof Element)
{
node_field_to_insert=(Element) the_node;
break;
}
}
3 go to the field node in the global XML
4 replace inside the node
// GET NODE field
NodeList nodes=document.getElementsByTagName("field");
for(int i=0; i<nodes.getLength(); i++)
{
Node the_node = nodes.item(i);
if(the_node instanceof Element)
{
Element a_child = (Element) the_node;
Node newNode = document.importNode(node_field_to_insert, true);
// FATHER
Node the_parent=a_child.getParentNode();
the_parent.replaceChild(newNode,a_child);
// WE STOP
break;
}
}

XPath read root element returns null

Given an XML file like:
<source>
<element value="a">
<element value="b">
</source>
I'm trying to read the root element ("source") of the XML using Java and XPath:
public String parseExpression(Document doc) {
NodeList nodeList = (NodeList) xPath.compile("/").evaluate(
doc, XPathConstants.NODESET);
return nodeList.item(0).getFirstChild().getNodeValue();
}
However it returns null. Why?
Because .getNodeValue(); does not return the value of the attribute. Try (Element)nodeList.item(0).getFirstChild()).getAttribute("value") instead.
The value you are trying to read is not in the element node you are accessing.
It is in a seperate attribute node which is only accessable when you cast your NodeList entry to Element.

How to add nodes from another xml using xmlbeans

I am using xmlbeans to generate the xml document, while I need to extract all the children from another xml file and insert them to my current document.
The to_be_add.xml:
<root>
<style>
.....
</style>
<atlas img="styles/jmap.png">
....
</atlas>
.....
</root>
And this xml file does not have a schema so I do not create related java class to map it. You think it as a plain xml file.
I want the style atlas node added. I use the following codes:
XmlObject pointRoot = XmlObject.Factory.parse(Main.class.getResourceAsStream("to_be_added.xml"));
NodeList nodeList = pointRoot.getDomNode().getChildNodes();
Node themeNode = renderthemeDoc.getDomNode();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
themeNode.appendChild(node);
}
Then I got error:
Exception in thread "main"
org.apache.xmlbeans.impl.store.DomImpl$WrongDocumentErr: Child to add
is from another document
And I found this post by searching "child to .... another document": how to add a xml document to another xml document in java which said that the connection between the element and the document has to be broken between the element can be add to other document.
So I try to build the Document object(that is why the variable pointDoc and themeDoc exist):
XmlObject pointRoot = XmlObject.Factory.parse(Main.class.getResourceAsStream("to_be_added.xml"));
Document pointDoc = pointRoot.getDomNode().getOwnerDocument();
System.out.println(pointDoc);
Element element = pointDoc.getDocumentElement();
NodeList nodeList = element.getChildNodes();
Document themeDoc = myCurrentDoc.getDomNode().getOwnerDocument();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
node = themeDoc.importNode(node, true);
themeDoc.appendChild(node);
}
Then I got NullPointerException which said that the pointDoc is null.
That is the whole process how I try to solve this problem. If it is unclear, please tell me, I will update accordingly.
Is it possible to fix it?
Since your other XML file is not mapped to a class, you can use a regular DOM parser to read it and extract its nodes. But using a generic object factory you can still get the nodes:
XmlObject pointRoot = XmlObject.Factory.parse( "<root>\n" +
" <style>\n" +
" </style>\n" +
" <atlas img=\"styles/jmap.png\">\n" +
" </atlas>\n" +
"</root>");
Node pointDoc = pointRoot.getDomNode().getFirstChild();
NodeList nodeList = pointDoc.getChildNodes();
for(int i = 0; i < nodeList.getLength(); i++) {
System.out.println("Node: " + nodeList.item(i).getNodeName());
}
This will print:
Node: #text
Node: style
Node: #text
Node: atlas
Node: #text

How to get XML content as a String

<root>
<h id="1">
<d value="1,2,3,4,5"><open>10:00</open><close>23:00</close></d>
<d value="6"><open>10:00</open><close>2:00</close></d>
<d value="7"><open>10:00</open><close>21:00</close></d>
</h>
<h id="2">
</h>
</root>
Here I have the XML which root has list of <h> tagged nodes. Now I need to break these into parts and set it into different variables (add into a map).
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(new InputSource(new ByteArrayInputStream(data.getBytes("utf-8"))));
NodeList nList = doc.getElementsByTagName("h");
for (int i = 0; i < nList.getLength(); i++)
{
Node nNode = nList.item(i);
System.out.println(nNode.getAttributes().getNamedItem("id") + " " + ?????);
}
what should I call in order to get the value (String value) of a nNode ?
Here is what Im looking for as the asnwer for the above code once some one fills the ????
1 <h id="1"><d value="1,2,3,4,5"><open>10:00</open><close>23:00</close></d><d value="6">open>10:00</open><close>2:00</close></d><d value="7"><open>10:00</open><close>21:00</close></d></h>
2 <h id="2"></h>
And i don't mind having as root element
You can use Node.getTextContent() to conveniently get all the text of a node (gets text of children as well).
See Parsing xml file contents without knowing xml file structure for a short example.
If you're trying to get the value attributes of the d nodes (I can't actually tell, your question is slightly unclear to me), then it would be different -- for that you would iterate through the children of each h node (use getChildNodes() or getFirstChild() + getNextSibling()) then grab their value attributes just as you are getting the id attribute of the h nodes (the above link also shows an example of iterating through child nodes).
Have you tried jDom library? http://www.jdom.org/docs/apidocs/org/jdom2/output/XMLOutputter.html
XMLOutputter outp = new XMLOutputter();
String s = outp.outputString(your_jdom_element);
Have you tried nNode.toString() if you are using Node from javax.xml.soap.Node.
You can use that:
http://docs.oracle.com/javase/7/docs/api/org/w3c/dom/Node.html#getTextContent()
but your sample nNode has other nodes, not just text. It seems you need helper method to construct String from child nodes.
Pass your nNode to nodeToString
XML Node to String in Java

Categories