I am having trouble parsing an xml file and retrieve data from it. Below is the xml and code snippet.
-----XML (test.xml)-----
<?xml version="1.0" encoding="utf-8"?>
<root>
<Server>
<IPAddress>xxx.xxx.xxx.xxx</IPAddress>
<UserName>admin</UserName>
<Password>admin</Password>
</Server>
-----Code Snippet: -----
public static String getInput(String element)
{
String value = "";
try {
File inputFile = new File("test.xml");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dbFactory.newDocumentBuilder();
Document inputData = builder.parse(inputFile);
inputData.getDocumentElement().normalize();
String[] elementArray = element.split("/");
XPath xPath = XPathFactory.newInstance().newXPath();
String xpathExpression = element;
System.out.println("Xpath Expression:" + xpathExpression);
NodeList node = (NodeList) xPath.compile(xpathExpression).evaluate(inputData, XPathConstants.NODESET);
System.out.println(node.getLength());
if (null != node){
System.out.println(node.getLength());
for (int i=0; i<node.getLength(); i++){
System.out.println(i);
System.out.println("Node count =" + node.getLength() + ";" +
"Node Name =" + node.item(i).getNodeName());
if (node.item(i).getNodeName() == elementArray[1]){
System.out.println(node.item(i).getNodeName()+ "=" + node.item(i).getNodeValue());
value = node.item(i).getNodeValue();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return value;
}
The code compiles OK. While running, it just doesn't seem to find the nodes "Server" and it's child "IPAddress". The call to getInput() above would come from main in the format below:
getInput("Server/IPAddress");
Not sure where it's going wrong and I am really new to Xpath. I was wondering if someone can help.
Thanks!
The outermost element is <root/>, not <server/>. Your query needs to be
getInput("root/Server/IPAddress")
if you want to use the full path, or even
getInput("/root/Server/IPAddress")
to indicate you're starting at the root element. Alternatively, you could have XPath to search for all server elements all over the document:
getInput("//Server/IPAddress")
All of those will output
Xpath Expression:root/Server/IPAddress
1
1
0
Node count =1;Node Name =IPAddress
instead of
Xpath Expression:Server/IPAddress
0
0
You could somehow prepend one of the prefixes of your choice in the getInput() function, of course.
Related
I use the worldweatheronline API. The service gives xml in the following form:
<hourly>
<tempC>-3</tempC>
<weatherDesc>rain</weatherDesc>
<precipMM>0.0</precipMM>
</hourly>
<hourly>
<tempC>5</tempC>
<weatherDesc>no</weatherDesc>
<precipMM>0.1</precipMM>
</hourly>
Can I somehow get all the nodes <hourly> in which <tempC>> 0 and <weatherDesc> = rain?
How to exclude from the response the nodes that are not interesting to me <hourly>?
This is quite feasible using XPath.
You can filter a document based on element values, attribute values and other criteria.
Here is a working example that gets the elements according to the first point in the question:
try (InputStream is = Files.newInputStream(Paths.get("C:/temp/test.xml"))) {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document xmlDocument = builder.parse(is);
XPath xPath = XPathFactory.newInstance().newXPath();
// get hourly elements that have tempC child element with value > 0 and weatherDesc child element with value = "rain"
String expression = "//hourly[tempC>0 and weatherDesc=\"rain\"]";
NodeList hours = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);
for (int i = 0; i < hours.getLength(); i++) {
System.out.println(hours.item(i) + " " + hours.item(i).getTextContent());
}
} catch (Exception e) {
e.printStackTrace();
}
I think you should create xsd from xml and generate JAXB classes.Using those JAXB class you can easily unmarshal the xml and process your logic.
I need help in make an xpath expression to read all node names, node values, and attributes in an xml string. I made this:
private List<String> listOne = new ArrayList<String>();
private List<String> listTwo = new ArrayList<String>();
public void read(String xml) {
try {
// Turn String into a Document
Document document = DocumentBuilderFactory.newInstance()
.newDocumentBuilder().parse(new ByteArrayInputStream(xml.getBytes()));
// Setup XPath to retrieve all tags and values
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nodeList = (NodeList) xPath.evaluate("//text()[normalize-space()='']", document, XPathConstants.NODESET);
// Iterate through nodes
for(int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
listOne.add(node.getNodeName());
listTwo.add(node.getNodeValue());
// Another list to hold attributes
}
} catch(Exception e) {
LogHandle.info(e.getMessage());
}
}
I found the expression //text()[normalize-space()=''] online; however, it doesn't work. When I get try to get the node name from listOne, it is just #text. I tried //, but that doesn't work either. If I had this XML:
<Data xmlns="Somenamespace.nsc">
<Test>blah</Test>
<Foo>bar</Foo>
<Date id="2">12242016</Date>
<Phone>
<Home>5555555555</Home>
<Mobile>5555556789</Mobile>
</Phone>
</Data>
listOne[0] should hold Data, listOne[1] should hold Test, listTwo[1] should hold blah, etc... All the attributes will be saved in another parallel list.
What expression should xPath evaluate?
Note: The XML String can have different tags, so I can't hard code anything.
Update: Tried this loop:
NodeList nodeList = (NodeList) xPath.evaluate("//*", document, XPathConstants.NODESET);
// Iterate through nodes
for(int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
listOne.add(i, node.getNodeName());
// If null then must be text node
if(node.getChildNodes() == null)
listTwo.add(i, node.getTextContent());
}
However, this only gets the root element Data, then just stops.
//* will select all element nodes, //#* all attribute nodes. However, an element node does not have a meaningful node value in the DOM, so you would need to read out getTextContent() instead of getNodeValue.
As you seem to consider an element with child elements to have a "null" value I think you need to check whether there are any child elements:
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
docBuilderFactory.setNamespaceAware(true);
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
Document doc = docBuilder.parse("sampleInput1.xml");
XPathFactory fact = XPathFactory.newInstance();
XPath xpath = fact.newXPath();
NodeList allElements = (NodeList)xpath.evaluate("//*", doc, XPathConstants.NODESET);
ArrayList<String> elementNames = new ArrayList<>();
ArrayList<String> elementValues = new ArrayList<>();
for (int i = 0; i < allElements.getLength(); i++)
{
Node currentElement = allElements.item(i);
elementNames.add(i, currentElement.getLocalName());
elementValues.add(i, xpath.evaluate("*", currentElement, XPathConstants.NODE) != null ? null : currentElement.getTextContent());
}
for (int i = 0; i < elementNames.size(); i++)
{
System.out.println("Name: " + elementNames.get(i) + "; value: " + (elementValues.get(i)));
}
For the sample input
<Data xmlns="Somenamespace.nsc">
<Test>blah</Test>
<Foo>bar</Foo>
<Date id="2">12242016</Date>
<Phone>
<Home>5555555555</Home>
<Mobile>5555556789</Mobile>
</Phone>
</Data>
the output is
Name: Data; value: null
Name: Test; value: blah
Name: Foo; value: bar
Name: Date; value: 12242016
Name: Phone; value: null
Name: Home; value: 5555555555
Name: Mobile; value: 5555556789
I have a simple .xml file and need to parse it. The file is the following:
<table name="agents">
<row name="agent" password="pass" login="agent" ext_uid="133"/>
</table>
I need to get values of name, password, login, ext_uid to create a DB record.
What I have done for this:
created an or.w3c.dom.Document:
public Document getDocument(String fileName){
DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
f.setValidating(false);
DocumentBuilder builder = f.newDocumentBuilder();
return builder.parse(new File(fileName));
}
next I'm trying to print values:
document = getDocument(fileName);
NodeList nodes = document.getChildNodes();
for (int i=0; i<nodes.getLength(); i++){
Node node = nodes.item(i);
if(node.getNodeType() == Node.ELEMENT_NODE){
NodeList listofNodes = node.getChildNodes();
for(int j=0; j<listofNodes.getLength(); j++){
if(node.getNodeType() == Node.ELEMENT_NODE){
Node childNode = listofNodes.item(j);
System.out.println(childNode.getNodeValue()+" " + childNode.getNodeName());
}
}
}
}
I use this because I'm trying to find out how to get values: childNode.getNodeValue()+" " + childNode.getNodeName()
but the result is the following:
#text
null row
#text
in the first and te third cases the NodeValue is empty and in the second case it is null, that means, I guess that there no NodeValue at all.
So my question is how to get values of name, password, login, ext_uid?
childNode.getNodeValue() is obviously null as its an empty tag. You have to look for attributes
Node childNode = listofNodes.item(j);
Element e = (Element)childNode;
String name = e.getAttribute("name");
String password= e.getAttribute("password");
String login= e.getAttribute("login");
String ext_uid= e.getAttribute("ext_uid");
The <row> element has no value, it only has attributes. If it had a value it would look more like <row>this would be the value returned from getNodeValue()</row>.
One way to get the data is to iterate the XML node attributes, for example:
NamedNodeMap attrs = childNode.getAttributes();
if (attrs != null) {
for (int k = 0; k < attrs.getLength(); k++) {
System.out.println("Attribute: "
+ attrs.item(k).getNodeName() + " = "
+ attrs.item(k).getNodeValue());
}
}
The output of your code is showing #text due to the carriage returns (\n characters) in the example XML file, which, according the specification, should be preserved. The null in the example output is the empty node value from the value-less <row> element.
Use XPath instead:
XPath xp = XPathFactory.newInstance().newXPath();
System.out.println(xp.evaluate("/table/row/#name", doc));
System.out.println(xp.evaluate("/table/row/#password", doc));
System.out.println(xp.evaluate("/table/row/#login", doc));
System.out.println(xp.evaluate("/table/row/#ext_uid", doc));
Best way to explain myself is to show you a piece of code:
This is my XML file I'm parsing:
<module>
<name>name1</name>
<type>type</type>
<content>
<p>This is some piece of code that should be treated as a full string, even that 'p' tag, because I want to use all content inside p tag for a webview in android.
</p>
<h1>This is a big classy title in html</h1>
</content>
</module>
As you can read in the p tag, basically I want to get the <content> tag's content and save it into a String to be treated. So at the end, I want to have a String initializated like:
String content = "<p> This is some piece.......</p> <h1>This is....</h1>";
This is my code that I'm using to get <name>, <type> values:
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(contingut);
doc.getDocumentElement().normalize();
NodeList nodes = doc.getElementsByTagName("module");
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
Element element = (Element) node;
if(element.getNodeType() == Element.ELEMENT_NODE){
System.out.println(getValue("name",element));
System.out.println(getContent("content",element));
}
}
private static String getValue(String tag, Element element) {
String value="";
try {
NodeList nodes = element.getElementsByTagName(tag).item(0).getChildNodes();
Node node = (Node) nodes.item(0);
value=node.getNodeValue();
} catch (Exception e){
value=null;
}
return value;
}
So for instance, when parsing, Name is printed correctly name1, but content is returning blank.
Any idea how can I get <content>'s content as a String?
Thank you.
EDIT
private static String getContent(String tag, Element element) {
String value="";
try {
Node nodes = element.getElementsByTagName(tag).item(0);
value = nodes.getTextContent();
} catch (Exception e){
value=null;
}
return value;
}
Log.d("debugging",getContent("content",element));
And this is printing this:
%20%20%20%20%20This%20some%20piece ....
It seems that it's not returning the string <p>.
Since getTextContent doesn't return any markup I think it won't be possible using any of the Node-methods.
The only way I see (if you want to use DocumentBuilder) is, that you write some code to rebuild the string out of the nodelists (iterate through nodes and node-attributes).
As a small sketch on what I mean: (only javalike pseudocode)
string rebuild(NodeList nodeList) {
string result = "";
for (Node n : nodeList) {
result += "<" + node.getNodeName() + " ";
NamedNodeMap aMap = node.getAttributes();
if (aMap != null) {
int aMapLength = aMap.getLength();
for (int i=0; i<aMapLength; ++i) {
Node a = aMap.item(i);
result += a.getNodeName() + "=" + a.getValue() + " ";
}
}
NodeList nList = node.getChildNodes();
if (nList == null) {
result += "/>";
} else {
result += ">";
result += rebuild(nList);
result += "</" + node.getNodeName() + ">";
}
}
return result;
}
You could also create a xsd file and to use xjc (JAXB) to create Java-classes. There are a lot of good tutorials out their on how to do this (depending on your IDE).
Then you could have everything marshaled/ unmarshaled by JAXB as you like.
Another way would be that you implement your own SaxHandler instead and use SAXParser and SAXParserFactory, which will be quite some work.
Use getTextContent() instead of getValue() function. Following is an example(same as yours getValue function).
private static String getContent(String tag, Element element) {
String value="";
try {
NodeList nodes = element.getElementsByTagName(tag).item(0).getChildNodes();
Node node = (Node) nodes.item(0);
value=node.getTextContent(); // notice getTextContent()
} catch (Exception e){
value=null;
}
return value;
}
It will work with well formatted xml
<module>
<name>name1</name>
<type>type</type>
<content>
<p>This is some piece of code that should be treated as a full string, even that 'p' tag, because I want to use all content inside p tag for a webview in android.
</p>
<h1>This is a big classy title in html</h1>
</content>
</module>
Following is the XML file -
<Country>
<Group>
<C>Tokyo</C>
<C>Beijing</C>
<C>Bangkok</C>
</Group>
<Group>
<C>New Delhi</C>
<C>Mumbai</C>
</Group>
<Group>
<C>Colombo</C>
</Group>
</Country>
I want to save the name of Cities to a text file using Java & XPath -
Below is the Java code which is unable to do the needful.
.....
.....
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse("Continent.xml");
XPath xpath = XPathFactory.newInstance().newXPath();
// XPath Query for showing all nodes value
XPathExpression expr = xpath.compile("//Country/Group");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
BufferedWriter out = new BufferedWriter(new FileWriter("Cities.txt"));
Node node;
for (int i = 0; i < nodes.getLength(); i++)
{
node = nodes.item(i);
String city = xpath.evaluate("C",node);
out.write(" " + city + "\r\n");
}
out.close();
.....
.....
Can somebody help me to get the required output?
You are getting only the first city because that's what you asked for. Your first XPATH expression returns all the Group nodes. You iterate over these and evaluate the XPATH C relative to each Group, returning a single city.
Just change the first XPATH to //Country/Group/C and eliminate the second XPATH altogether -- just print the text value of each node returned by the first XPATH.
I.e.:
XPathExpression expr = xpath.compile("//Country/Group/C");
...
for (int i = 0; i < nodes.getLength(); i++)
{
node = nodes.item(i);
out.write(" " + node.getTextContent() + "\n");
}