Unexpected results from Java DOM getAttribute - java

I am having some trouble with my DOM parser for Java. I am trying to parse the entire XML document and save everything into either Strings or ArrayLists depending how it appears in the document. Right now I have run into one issue in particular. I am trying to parse the attribute of a child value of an element here:
-<Ntfctn>
<Id>161RD2521JI00XSZ</Id>
<CreDtTm>2016-01-27T13:25:21</CreDtTm>
+<Acct>
+<TxsSummry>
+<Ntry xmlns="urn:iso:std:iso:20022:tech:xsd:camt.054.001.04">
As you can see there are two elements under Ntfctn that contain attributes, Id and CreDtTm. Right now all I really need is "Id". I have already collected the credit amount earlier in my code. There are numerous "Id" tags throughout the XML so calling "Id" directly won't work.
I have tried calling the attribute in this way:
NodeList Ntfctn = doc.getElementsByTagName("Ntfctn");
for(int temp = 0; temp < MsgRcpt.getLength(); temp++){
System.out.println(Ntfctn.item(0).getAttributes());
}
But get some unexpected results
com.sun.org.apache.xerces.internal.dom.AttributeMap#2a139a55
I've tried changing the Ntfctn.get value to several numbers but none seem to pull what I am looking for. Other areas of my code have worked perfectly. I will post the entire thing below.
Please let me know the proper way for me to get the value from an attribute that has the same name across the file.
package parsing;
import java.io.File;
import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.*;
public class XMLParser
{
public static String CreDtTmS;
public static String MsgIdS;
public static String MsgRcptS;
public static String NtfctnS;
public void parseXML(){
try{
File inputFile = new File("C:\\Users\\jhamric\\Desktop\\Camt54.xml");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(inputFile);
doc.getDocumentElement().normalize();
NodeList CreDtTm = doc.getElementsByTagName("CreDtTm");
for(int temp = 0; temp < CreDtTm.getLength(); temp++){
CreDtTmS = CreDtTm.item(0).getTextContent();
}
NodeList MsgId = doc.getElementsByTagName("MsgId");
for(int temp = 0; temp < MsgId.getLength(); temp++){
MsgIdS = MsgId.item(0).getTextContent();
}
NodeList MsgRcpt = doc.getElementsByTagName("MsgRcpt");
for(int temp = 0; temp < MsgRcpt.getLength(); temp++){
MsgRcptS = MsgRcpt.item(0).getTextContent();
}
NodeList Ntfctn = doc.getElementsByTagName("Ntfctn");
for(int temp = 0; temp < MsgRcpt.getLength(); temp++){
System.out.println(Ntfctn.item(0).getAttributes());
}
}catch (Exception e){
e.printStackTrace();
}
System.out.println("GrpHdr -> CreDtTm: "+CreDtTmS);
System.out.println("GrpHdr -> MsgId: "+MsgIdS);
System.out.println("GrpHdr -> MsgRcpt -> Id -> OrgId -> Othr -> Id: "+MsgRcptS);
}
}
The question was answered using help from the accepted answer below.
After trial and error this was the bit of code that worked:
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPath xPath =XPathFactory.newInstance().newXPath();
XPathExpression expr = xpath.compile("//Ntfctn/Id");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
System.out.println(nodes.getLength());
for(int i = 0; i < nodes.getLength(); i++){
Element el = (Element) nodes.item(i);
System.out.println("Tag: "+ el.getNodeName());
System.out.println(el.getTextContent());
}

To refine your selection you can use xpath:
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nodes = (NodeList)xPath.evaluate("//Ntfctn/Id",
doc.getDocumentElement(), XPathConstants.NODESET);

Related

Parsing an XML document to get node values

I have an xml structure as below:
String attributesXML="<entry>
<value>
<List>
<String>Rob</String>
<String>Mark</String>
<String>Peter</String>
<String>John</String>
</List>
</value>
</entry>"
I want to fetch the values Rob,Mark,Peter,John. I can get the nodes starting from entry node(Code below). Problem is i don't know what will be the child node names under entry node. Starting from entry node i need to keep drilling down until I find the values. I have written a method getChildNodeValue() but it doesn't give me the required Output. It does print what i need but it prints some extra stuff as well. I need to return the values as a csv from this method getChildNodeValue().
Getting Entry Node:
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(attributesXML));
Document doc = db.parse(is);
NodeList nodes = doc.getElementsByTagName("entry");
for (int i = 0; i < nodes.getLength(); i++) {
if(nodes.item(i).hasChildNodes()){
getChildNodeValue(nodes.item(i));
}
}
public static void getChildNodeValue(Node node) {
System.out.println("Start Node: "+node.getNodeName());
NodeList nodeList = node.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node currentNode = nodeList.item(i);
while(currentNode.hasChildNodes()){
System.out.println("Current Node: "+currentNode.getNodeName());
nodeList = currentNode.getChildNodes();
for(int j=0;j<nodeList.getLength();j++){
currentNode = nodeList.item(j);
System.out.println("Node name: "+currentNode.getNodeName());
System.out.println("Node value: "+currentNode.getTextContent());
}
}
}
}
you can simply use XStream library for xml parsing it will parse java object to xml and vice versa.
check out below link
http://x-stream.github.io/tutorial.html

Java XML XPath Full XML

got a little problem. I have the following code:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse("result1.xml");
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
XPathExpression expr = xpath.compile("//element");
String elements = (String) expr.evaluate(doc, XPathConstants.STRING);
What i get :
jcruz0#exblog.jp
Cheryl
Blake
195115
What i want:
<person>
<email>jcruz0#exblog.jp</email>
<firstname>Cheryl</firstname>
<lastname>Blake</lastname>
<number>195115</number>
</person>
So as you can see i want the full XML tree. Not just the NodeValue.
Maybe somebody knows the trick.
Thanks for any help.
You got the string value of the selected XML element because you specified XPathConstants.STRING to XPathExpression.evaluate().
Instead, specify a return type of XPathConstants.NODE if you know for sure that your XPath will select a single element,
String elements = (String) expr.evaluate(doc, XPathConstants.NODE);
or XPathConstants.NODESET for multiple elements, which you would then iterate over to process as necessary.
Something like this can be done.
XPathExpression expr = xpath.compile("/person");
NodeList elements = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
for (int i = 0; i < elements.getLength(); i++) {
// the person node
System.out.println(elements.item(i).getNodeName());
for (int x = 0; x < elements.item(i).getChildNodes().getLength(); x++) {
// the elements under person
if (elements.item(i).getChildNodes().item(x).getNodeType() == Node.ELEMENT_NODE) {
System.out.println("\t" + elements.item(i).getChildNodes().item(x).getNodeName() + " - " + elements.item(i).getChildNodes().item(x).getTextContent());
}
}
}
Output
person
email - jcruz0#exblog.jp
firstname - Cheryl
lastname - Blake
number - 195115
You can use the nodes to do what you want, or wrap them in < and > if you just want to print them.

Android/Java XML Parsing with nodes of same name

I need some advice on how to parse XML with Java where there are multiple nodes that have the same tag. For example, if I have an XML file that looks like this:
<?xml version="1.0"?>
<TrackResponse>
<TrackInfo ID="EJ958083578US">
<TrackSummary>Your item was delivered at 8:10 am on June 1 in Wilmington DE 19801.</TrackSummary>
<TrackDetail>May 30 11:07 am NOTICE LEFT WILMINGTON DE 19801.</TrackDetail>
<TrackDetail>May 30 10:08 am ARRIVAL AT UNIT WILMINGTON DE 19850.</TrackDetail>
<TrackDetail>May 29 9:55 am ACCEPT OR PICKUP EDGEWATER NJ 07020.</TrackDetail>
</TrackInfo>
</TrackResponse>
I am able to get the "TrackSummary" but I do not know how to handle the "TrackDetail", since there is more than 1. There could be more than the 3 on that sample XML so I need a way to handle that.
So far I have this code:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource is = new InputSource(new StringReader(xmlResponse));
Document dom = builder.parse(is);
//Get the ROOT: "TrackResponse"
Element docEle = dom.getDocumentElement();
//Get the CHILD: "TrackInfo"
NodeList nl = docEle.getElementsByTagName("TrackInfo");
String summary = "";
//Make sure we found the child node okay
if (nl != null && nl.getLength() > 0)
{
//In the event that there is more then one node, loop
for (int i = 0 ; i < nl.getLength(); i++)
{
summary = getTextValue(docEle,"TrackSummary");
Log.d("SUMMARY", summary);
}
return summary;
}
How would I handle the whole 'multiple TrackDetail nodes' ordeal? I'm new to XML parsing so I am a bit unfamiliar on how to tackle things like this.
You can try like this :
public Map getValue(Element element, String str) {
NodeList n = element.getElementsByTagName(str);
for (int i = 0; i < n.getLength(); i++) {
System.out.println(getElementValue(n.item(i)));
}
return list/MapHere;
}
If you are free to change your implementation then i would suggest you to use implementation given here.
you can collect the trackdetail in string array and when you are in XmlPullParser.END_TAG check for trackinfo tag end and then stop
You can refer below code for that.
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(f);
Element root = doc.getDocumentElement();
NodeList nodeList = doc.getElementsByTagName("TrackInfo");
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i); // this is node under track info
// do your stuff
}
for more information you can go through below link.
How to parse same name tag in xml using dom parser java?
It may help.

How do I resolve two nodes which have the same name but under different parents?

<PublicRecords>
<USBankruptcies>
<USBanktruptcy>...<USBankruptcy>
<CourtId>...</CourtId>
<USBanktruptcy>...<USBankruptcy>
<CourtId>...</CourtId>
</USBankruptcies>
<USTaxLiens>
<USTaxLien>...<USTaxLien>
<CourtId>...</CourtId>
<USTaxLien>...<USTaxLien>
<CourtId>...</CourtId>
</USTaxLiens>
<USLegalItems>
<USLegalItem><USLegalItem>
<CourtId></CourtId>
<USLegalItem><USLegalItem>
<CourtId></CourtId>
</USLegalItems>
</PubicRecords>
I am using a combination of doc and xpath objects to extract the attributes and node contents.
NodeList bp = doc.getElementsByTagName("USBankruptcy");
NodeList nl = doc.getElementsByTagName("CourtId");
long itrBP;
for (itrBP = 0; itrBP < bp.getLength(); itrBP++ )
{
Element docElement = (Element) bp.item(itrBP);
Element courtElement = (Element) nl.item(itrBP);
NodeList df = docElement.getElementsByTagName("DateFiled");
if(df.getLength() > 0)
{
dateFiled = nullIfBlank(((Element)df.item(0)).getFirstChild().getTextContent());
dateFiled = df.format(dateFiled);
}
But, when I say get elements of tag name CourtID, it will get all the CourtIDs, not just the ones under USBankruptcy.
Is there any way to specify the parent?
I tried NodeList nl = doc.getElementsByTagName("USBankruptcies/CourtId");
It gave me a dom error on run time.
Rather than calling the getElementsByTagName("CourtId") method on the Document, call it on the child Element (in your case, the <USBankruptcies> element).
NodeList bankruptcyNodes = doc.getElementsByTagName("USBankruptcies");
Element bankruptcyElement = (Element) bankruptcyNodes.item(0);
NodeList bankruptcyCourtNodes = bankruptcyElement.getElementsByTagName("CourtId");
// etc...
Please find the code here:
DocumentBuilderFactory domFactory = DocumentBuilderFactory
.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse("test.xml");
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression expr = xpath.compile("*//USBankruptcies/CourtId");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i));
}

JAXB: Why does a value return null if I just set that value, and returns a real value when I set other values?

I have generadet a java class from a xsd schema with JAXB.
In the the Main class I have a method called recursiveNodeList(NodeList list) that just takes a node list and iterates through it recursivly to get all values out of it.
Everything works except one thing that I cannot simply understand.
In the code below I have these two lines:
item.setNote("Notetest1");
item.setTitle("Title1");
When I run the code I get this output:
title->#text->Title1
note->#text->Notetest1
If I just use one of the lines, like:
item.setNote("Notetest1");
// item.setTitle("Title1"); /*commented out*/
I get this output:
item->note->null
Why is the note null if I just set that value and not call setTitle() and why does it have a value when I call both setNote and setTitle?
The code in its whole:
public class JavaXML {
public static void main(String[] args) throws ParserConfigurationException, JAXBException, FileNotFoundException {
Item item = new Item();
JAXBContext jaxb = JAXBContext.newInstance(item.getClass().getPackage().getName());
Marshaller marshaller = jaxb.createMarshaller();
item.setNote("Notetest1");
item.setTitle("Title1");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.newDocument();
marshaller.marshal(item, doc);
NodeList nodeList = doc.getChildNodes();
recursiveNodeList(nodeList);
}
public static void recursiveNodeList(NodeList nodeList) {
for(int i = 0; i< nodeList.getLength(); i++) {
Node fstNode = nodeList.item(i);
if (fstNode.getNodeType() == Node.ELEMENT_NODE) {
Element fstElmnt = (Element) fstNode;
if(fstElmnt.getChildNodes().getLength() > 1) {
NodeList fstNmElmntLst = fstElmnt.getChildNodes();
recursiveNodeList(fstNmElmntLst);
} else {
NodeList fstNmElmntLst = fstElmnt.getChildNodes();
if(((Node)fstNmElmntLst.item(0)) != null)
System.out.println(fstNode.getNodeName()+"->"+((Node)fstNmElmntLst.item(0)).getNodeName() + "->"+((Node)fstNmElmntLst.item(0)).getNodeValue());
}
}
}
}
}
EDIT
Another question:
If I instead of setting the title and note, set the category like this:
Category category = new Category();
category.setStringOne("string1");
category.setStringTwo("string2");
item.setCategory(category);
Then the output would be:
item->category->string1string2
Is there any way to get the "string1" and "string2" values into separate variables without using string manipulation techniques?
The error is in your recursiveNodeList method. In the single element case you were hitting the System.out.println line with an Element node, and in the two element case you were hitting the System.out.println line with a Text node. The code below will work, but probably needs cleaned up.
public static void recursiveNodeList(NodeList nodeList) {
for(int i = 0; i< nodeList.getLength(); i++) {
Node fstNode = nodeList.item(i);
if (fstNode.getNodeType() == Node.ELEMENT_NODE) {
Element fstElmnt = (Element) fstNode;
if(fstElmnt.getChildNodes().getLength() > 1) {
NodeList fstNmElmntLst = fstElmnt.getChildNodes();
recursiveNodeList(fstNmElmntLst);
} else {
NodeList fstNmElmntLst = fstElmnt.getChildNodes();
Node node = fstNmElmntLst.item(0);
if(node != null)
if(node.getNodeType() == Node.ELEMENT_NODE) {
System.out.println(fstNode.getNodeName()+"->"+node.getNodeName() + "->"+((Element)node).getTextContent());
} else {
System.out.println(fstNode.getNodeName()+"->"+node.getNodeName() + "->"+node.getNodeValue());
}
}
}
}
}
}
UPDATE
Your JAXB (JSR-222) implementation is creating the document correctly. The original and updated errors you are seeing are due to the way you are processing the DOM nodes in recursiveNodeList. If you are interested in continuing with that approach I would recommend stepping through the code and paying attention to when the current node corresponds to a tag (i.e. note) and is of type Element, and when it corresponds to text (i.e. Notetest1) and is of type Text. Below I have given a new code example that uses XPath to introspect the document which you may find easier to use.
package forum9698306;
import javax.xml.bind.*;
import javax.xml.parsers.*;
import javax.xml.xpath.*;
import org.w3c.dom.*;
public class JavaXML {
public static void main(String[] args) throws Exception {
Item item = new Item();
JAXBContext jaxb = JAXBContext.newInstance(item.getClass().getPackage().getName());
Marshaller marshaller = jaxb.createMarshaller();
item.setNote("Notetest1");
item.setTitle("Title1");
Category category = new Category();
category.setStringOne("string1");
category.setStringTwo("string2");
item.setCategory(category);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.newDocument();
marshaller.marshal(item, doc);
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
System.out.println(xpath.evaluate("item/note/text()", doc, XPathConstants.STRING));
System.out.println(xpath.evaluate("item/title/text()", doc, XPathConstants.STRING));
System.out.println(xpath.evaluate("item/category/stringOne/text()", doc, XPathConstants.STRING));
System.out.println(xpath.evaluate("item/category/stringTwo/text()", doc, XPathConstants.STRING));
}
}
Output
Notetest1
Title1
string1
string2

Categories

Resources