How to update empty Node value using Xpath java? - 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.

Related

Get XML child node from parent node using XPATH 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()

Why does getLocalName() return null?

I'm loading some XML string like this:
Document doc = getDocumentBuilder().parse(new InputSource(new StringReader(xml)));
Later, I extract a node from this Document:
XPath xpath = getXPathFactory().newXPath();
XPathExpression expr = xpath.compile(expressionXPATH);
NodeList nodeList = (NodeList)expr.evaluate(doc, XPathConstants.NODESET);
Node node = nodeList.item(0);
Now I want to get the local name of this node but I get null.
node.getLocalName(); // return null
With the debugger, I saw that my node has the following type: DOCUMENT_POSITION_DISCONNECTED.
The Javadoc states that getLocalName() returns null for this type of node.
Why node is of type DOCUMENT_POSITION_DISCONNECTED and not ELEMENT_NODE?
How to "convert" the type of the node?
As the documentation https://docs.oracle.com/javase/7/docs/api/org/w3c/dom/Node.html#getLocalName() states:
for nodes created with a DOM Level 1 method, [...] this is always null
so make sure you use a namespace aware DocumentBuilderFactory with setNamespaceAware(true), that way the DOM is supporting the namespace aware DOM Level 2/3 and will have a non-null value for getLocalName().
A simple test program
String xml = "<root/>";
DocumentBuilderFactory db = DocumentBuilderFactory.newInstance();
Document dom1 = db.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
System.out.println(dom1.getDocumentElement().getLocalName() == null);
db.setNamespaceAware(true);
Document dom2 = db.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
System.out.println(dom2.getDocumentElement().getLocalName() == null);
outputs
true
false
so (at least) the local name problem you have is caused by using a DOM Level 1, not namespace aware document (builder factory).

Fetch node value containing escape characters using xpath

I am using Java XML API to fetch a node value for a given XPath.
Here is the code I am using to fetch the node value for a given XPath
final XPathFactory factory = XPathFactory.newInstance();
final XPath xpath1 = factory.newXPath();
xpath1.setNamespaceContext(new MyNamespaceContext());
InputSource inputSource = new InputSource(extractData(fileName));
inputSource.setEncoding("UTF-8");
String nodeValue = xpath1.evaluate(xpath, inputSource);
The xml file contains the node value as Some>data
The node value I am expecting is Some>data but the value that is returned from the above code is Some>data
Can any one help how to change the above code so that i get the node data as Some>data

How to retrieve a specific node's value in XPath?

I have a XML file with this format:
<object>
<origin>1:1:1</origin>
<normal>2:2:2</normal>
<leafs>
<object>
<origin>1:1:1</origin>
<normal>3:3:3</normal>
<leafs>none</leafs>
</object>
</leafs>
</object>
How could I retrieve the value "none" of element <leafs> on second level of the tree? I used this
XPathExpression expLeafs = xpath.compile("*[name()='leafs']");
Object resLeafs = expLeafs.evaluate(node, XPathConstants.NODESET);
NodeList leafsList = (NodeList) resLeafs;
if (!leafsList.item(0).getFirstChild().getNodeValue().equals("none"))
more code...
but it doesn't work because there are some empty text nodes bofore and after "none". Is there a way to deal with it like xpath.compile("*[value()='none']")?
I just ran a simple test program using your XML file and
expr = xpath.compile("/object/leafs/object/leafs/text()");
and got the desired "none" result. If you have additional requirements, you'll have to edit your question.
After a checking the code line #Lord Torgamus provided i managed to parse the document as i needed like this:
XPathExpression expLeafs = xpath.compile("*[name()='leafs']");
Object resLeafs = expLeafs.evaluate(node, XPathConstants.NODESET);
NodeList leafsList = (NodeList) resLeafs;
Node nd = leafsList.item(0);
XPathExpression expr = xpath.compile("text()");
Object resultObj = expr.evaluate(nd, XPathConstants.NODE);
String str = expr.evaluate(nd).trim();
System.out.println(str);
and the output is "none" with no other empty text node.

parsing XML tag with no data. getting NullPointerException

I have an xml with no data (for example the data for remark tag), so I try to update the contents of the remark tag, but I get a NullPointerException.
Here is teh sample code that I use.
NodeList itemCheckedNodeList = positionElement.getElementsByTagName("remark");
Element itemCheckedElement = (Element) itemCheckedNodeList.item(0);
NodeList itemCheckedLN = itemCheckedElement.getChildNodes();
Text itemCheckedText = (Text)itemCheckedLN.item(0);
itemCheckedText.setTextContent("Here is a new comment");
but I get a exception at "itemCheckedText.setTextContent(comments);"
<events>
<event>
<date>Some date here</date>
<time>Some time here</time>
<remark>Something about the event</remark>
</event>
<event>
<date>Some date here</date>
<time>Some time here</time>
<remark></remark>
</event>
</events>
Does anyone have the solution for this?
You'll need to add a null check for empty text nodes and create them as necessary:
NodeList itemCheckedNodeList = positionElement.getElementsByTagName("remark");
Element itemCheckedElement = (Element) itemCheckedNodeList.item(0);
NodeList itemCheckedLN = itemCheckedElement.getChildNodes();
Text itemCheckedText = (Text) itemCheckedLN.item(0);
if (itemCheckedText == null) {
Document doc = itemCheckedElement.getOwnerDocument();
itemCheckedText = doc.createTextNode("remark");
itemCheckedElement.appendChild(itemCheckedText);
}
itemCheckedText.setTextContent("Here is a new comment");
Text between elements are represented as node children. An empty element probably do not have a text node child, so you have to
get the remark
check if there is a text node child, if not create it
set the text

Categories