Hello I am getting an unexpected error, please help me out!?
I want to search for the name of a person and display all the available information about him.
In the following code I am trying to find the person with first name Ivan and this "translate" is copied from other xml topic in stackoverflow as a incase-sensitive option.
public static void main(String[] args) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory
.newInstance();
Document doc = factory.newDocumentBuilder().parse(
new File("staff.xml"));
XPathFactory xFactory = XPathFactory.newInstance();
XPath xPath = xFactory.newXPath();
XPathExpression exp = xPath
.compile("/staff/person/name/firstName[contains(translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'Ivan')]");
NodeList nl = (NodeList) exp.evaluate(doc.getFirstChild(),
XPathConstants.NODESET);
for (int index = 0; index < nl.getLength(); index++) {
Node node = nl.item(index);
System.out.println(node.getTextContent());
}
} catch (Exception ex) {
Logger.getLogger(TestXML05.class.getName()).log(Level.SEVERE, null,
ex);
}
}
And this is my XML example file:
<?xml version="1.0" encoding="utf-8"?>
<staff>
<person id="1" role="chief">
<name>
<firstName>Ivan</firstName>
<lastName>Popov</lastName>
</name>
<phone>
<phoneOne>0273090909</phoneOne>
<phoneTwo>0878123456</phoneTwo>
</phone>
<email>i.popov#fdiba.tu-sofia.bg</email>
<room>10202</room>
<title>Dr.Ing.</title>
</person>
<person id="2" role="dozent">
<name>
<firstName>Georgi</firstName>
<lastName>Ivanov</lastName>
</name>
<phone>
<phoneOne>029988115</phoneOne>
<phoneTwo>0888123333</phoneTwo>
</phone>
<email>g.ivanov#fdiba.tu-sofia.bg</email>
<room>10203</room>
<title>Dr.Ing.</title>
</person>
<person id="3" role="assistent">
<name>
<firstName>Petur</firstName>
<lastName>Kirilov</lastName>
</name>
<phone>
<phoneOne>028773455</phoneOne>
<phoneTwo>0898448576</phoneTwo>
</phone>
<email>p.kirilov#fdiba.tu-sofia.bg</email>
<room>10308</room>
<title>Ing.</title>
</person>
</staff>
Your xpath expression seems to be incorrect. You need to change the xpath expression to /staff/person/name/firstName[contains(text(),'Georgi')]/../... This selects person node corresponding to the person with the first name Georgi.
public static void main(String[] args) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory
.newInstance();
Document doc = factory.newDocumentBuilder().parse(
new File("src/resources/staff.xml"));
XPathFactory xFactory = XPathFactory.newInstance();
XPath xPath = xFactory.newXPath();
XPathExpression exp = xPath
.compile("/staff/person/name/firstName[contains(text(),'Georgi')]/../..");
NodeList nl = (NodeList) exp.evaluate(doc,
XPathConstants.NODESET);
for (int index = 0; index < nl.getLength(); index++) {
Node node = nl.item(index);
if (node.hasAttributes()) {
Attr attr = (Attr) node.getAttributes().getNamedItem("role");
if (attr != null) {
String attribute= attr.getValue();
System.out.println("Person role : " + attribute);
}
}
System.out.println(node.getTextContent());
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
Related
I'm trying to read from xml by this code and its not working .. I need to read from id in the specific tag (getelementbyid)
what I need to change in my java code
THIS is my xml code
<book>
<person>
<name>
<first id=1 name="Kiran"/>
<first id=2 name="Pai"/>
</name>
<age>22</age>
</person>
<person>
<name>
<first id=1 name="Bill"/>
<first id=2 name="Gates"/>
</name>
<age>46</age>
</person>
<person>
<name>
<first id=1 name="Steve"/>
<first id=2 name="Jobs"/>
</name>
<age>40</age>
</person>
</book>
and this is my java code
import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class XMLReader {
public static void main(String argv[]) {
try {
File file = new File("c:\\MyXMLFile.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(file);
doc.getDocumentElement().normalize();
System.out.println("Root element " + doc.getDocumentElement().getNodeName());
NodeList nodeLst = doc.getElementsByTagName("employee");
System.out.println("Information of all employees");
for (int s = 0; s < nodeLst.getLength(); s++) {
Node fstNode = nodeLst.item(s);
if (fstNode.getNodeType() == Node.ELEMENT_NODE) {
Element fstElmnt = (Element) fstNode;
NodeList fstNmElmntLst = fstElmnt.getElementsByTagName("firstname");
Element fstNmElmnt = (Element) fstNmElmntLst.item(0);
NodeList fstNm = fstNmElmnt.getChildNodes();
System.out.println("First Name : " + ((Node) fstNm.item(0)).getNodeValue());
NodeList lstNmElmntLst = fstElmnt.getElementsByTagName("lastname");
Element lstNmElmnt = (Element) lstNmElmntLst.item(0);
NodeList lstNm = lstNmElmnt.getChildNodes();
System.out.println("Last Name : " + ((Node) lstNm.item(0)).getNodeValue());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
You can take a look at JAXB. It is part of Java SE. You only need to map your XML schema to few POJO classes and describe them with JAXB annotations.
Possible code to map an XML to an object, and vice versa, will take ~5 lines.
You can read about it here: http://docs.oracle.com/javase/tutorial/jaxb/intro/
This should work:
public class XMLReader {
public static void main(String argv[]) {
try {
File file = new File("c:\\MyXMLFile.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(file);
doc.getDocumentElement().normalize();
System.out.println("Root element " + doc.getDocumentElement().getNodeName());
NodeList nodeLst = doc.getElementsByTagName("name");
System.out.println("Information of all employees");
for (int s = 0; s < nodeLst.getLength(); s++) {
Node fstNode = nodeLst.item(s);
if (fstNode.getNodeType() == Node.ELEMENT_NODE) {
Element fstElmnt = (Element) fstNode;
NodeList fstNmElmntLst = fstElmnt.getElementsByTagName("first");
Element fstNmElmnt = (Element) fstNmElmntLst.item(0);
System.out.println("First Name : " +fstNmElmnt.getAttributeNode("name").getNodeValue());
Element lstNmElmnt = (Element) fstNmElmntLst.item(1);
System.out.println("Last Name : " +lstNmElmnt.getAttributeNode("name").getNodeValue());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
And correct your xml to quote the attributes:
<book>
<person>
<name>
<first id="1" name="Kiran"/>
<first id="2" name="Pai"/>
</name>
<age>22</age>
</person>
<person>
<name>
<first id="1" name="Bill"/>
<first id="2" name="Gates"/>
</name>
<age>46</age>
</person>
<person>
<name>
<first id="1" name="Steve"/>
<first id="2" name="Jobs"/>
</name>
<age>40</age>
</person>
</book>
One of the problems is that the built in Java XML libraries are only so-so. While meant primarily for for HTML, if you are not using xpaths, JSoup is a lot easier to work with.
String yourXml = ...
Document doc = Jsoup.parse(yourXml, "", Parser.xmlParser());
for (Element person : doc.getElementsByTag("person")) {
String firstName = person.getElementById("1").text();
String lastName = person.getElementById("2").text();
}
I have the bellow xml:
<modelingOutput>
<listOfTopics>
<topic id="1">
<token id="354">wish</token>
</topic>
</listOfTopics>
<rankedDocs>
<topic id="1">
<documents>
<document id="1" numWords="0"/>
<document id="2" numWords="1"/>
<document id="3" numWords="2"/>
</documents>
</topic>
</rankedDocs>
<listOfDocs>
<documents>
<document id="1">
<topic id="1" percentage="4.790644689978203%"/>
<topic id="2" percentage="11.427632949428334%"/>
<topic id="3" percentage="17.86913349249596%"/>
</document>
</documents>
</listOfDocs>
</modelingOutput>
Ι Want to parse this xml file and get the topic id and percentage from ListofDocs
The first way is to get all document element from xml and then I check if grandfather node is ListofDocs.
But the element document exist in rankedDocs and in listOfDocs, so I have a very large list.
So I wonder if exist better solution to parse this xml avoiding if statement?
My code:
public void parse(){
Document dom = null;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
InputSource is = new InputSource(new StringReader(xml));
dom = db.parse(is);
Element doc = dom.getDocumentElement();
NodeList documentnl = doc.getElementsByTagName("document");
for (int i = 1; i <= documentnl.getLength(); i++) {
Node item = documentnl.item(i);
Node parentNode = item.getParentNode();
Node grandpNode = parentNode.getParentNode();
if(grandpNode.getNodeName() == "listOfDocs"{
//get value
}
}
}
First, when checking the node name you shouldn't compare Strings using ==. Always use the equals method instead.
You can use XPath to evaluate only the document topic elements under listOfDocs:
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xPath = xPathFactory.newXPath();
XPathExpression xPathExpression = xPath.compile("//listOfDocs//document/topic");
NodeList topicnl = (NodeList) xPathExpression.evaluate(dom, XPathConstants.NODESET);
for(int i = 0; i < topicnl.getLength(); i++) {
...
If you do not want to use the if statement you can use XPath to get the element you need directly.
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse("source.xml");
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
XPathExpression expr = xpath.compile("/*/listOfDocs/documents/document/topic");
NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i).getAttributes().getNamedItem("id"));
System.out.println(nodes.item(i).getAttributes().getNamedItem("percentage"));
}
Please check GitHub project here.
Hope this helps.
I like to use XMLBeam for such tasks:
public class Answer {
#XBDocURL("resource://data.xml")
public interface DataProjection {
public interface Topic {
#XBRead("./#id")
int getID();
#XBRead("./#percentage")
String getPercentage();
}
#XBRead("/modelingOutput/listOfDocs//document/topic")
List<Topic> getTopics();
}
public static void main(final String[] args) throws IOException {
final DataProjection dataProjection = new XBProjector().io().fromURLAnnotation(DataProjection.class);
for (Topic topic : dataProjection.getTopics()) {
System.out.println(topic.getID() + ": " + topic.getPercentage());
}
}
}
There is even a convenient way to convert the percentage to float or double. Tell me if you like to have an example.
I use XML XPath API in my application
This is my soap request
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tes="http://testwork/">
<soapenv:Header/>
<soapenv:Body>
<tes:sayHelloWorldFrom>
<!--Optional:-->
<arg0>value</arg0>
</tes:sayHelloWorldFrom>
</soapenv:Body>
</soapenv:Envelope>
I want to retrieve the body from this message, thus I want to have
<soapenv:Body>
<tes:sayHelloWorldFrom>
<!--Optional:-->
<arg0>value</arg0>
</tes:sayHelloWorldFrom>
</soapenv:Body>
My piece of code looks like
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
org.w3c.dom.Document doc = null;
try {
doc = factory.newDocumentBuilder().parse(is);
XPathFactory xFactory = XPathFactory.newInstance();
XPath xPath = xFactory.newXPath();
Object result = xPath.compile("/soapenv:Envelope/soapenv:Body").evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
log.info("result " + nodes);
But the result is result com.sun.org.apache.xml.internal.dtm.ref.DTMNodeList#19f76837
So what am I doing wrong?
XPathConstants.NODESET instructs the API to return a NodeList of the results it finds matching the query.
This is useful when you are expecting a variable number of matches. You can iterate over the list...
for (int index = 0; index < nodes.getLength(); index++) {
Node node = nodes.item(index);
//...
}
If you are confident that you will only receive a single result (or you just want the first match), you can use XPathConstants.NODE instead
Object result = xPath.compile("/soapenv:Envelope/soapenv:Body").evaluate(doc, XPathConstants.NODE);
Node node = (Node)result;
Updated
There's probably away to do this without doing the following, but name spaces do my head...
After you create the factory, set it's name space awareness tofalse`, then drop the node name space context from your search, for example...
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(false);
org.w3c.dom.Document doc = null;
try {
doc = factory.newDocumentBuilder().parse(new File("Soap.xml"));
XPathFactory xFactory = XPathFactory.newInstance();
XPath xPath = xFactory.newXPath();
Object result = xPath.compile("/Envelope/Body").evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
System.out.println("Found " + nodes.getLength() + " matches");
for (int index = 0; index < nodes.getLength(); index++) {
Node node = nodes.item(index);
System.out.println(node);
}
} catch (ParserConfigurationException | SAXException | IOException | XPathExpressionException exp) {
exp.printStackTrace();
}
I have this XML:
<root>
<items>
<item1>
<tag1>1</tag1>
<sub>
<sub1>10 </sub1>
<sub2>20 </sub2>
</sub>
</item1>
<item2>
<tag1>1</tag1>
<sub>
<sub1> </sub1>
<sub2> </sub2>
</sub>
</item2>
</items>
</root>
I want to get the item1 element and the name and values of the child elements.
That is, i want to get: tag1 - 1,sub1-10,sub2-20.
How can i do this? so far i can only get elements without children.
Document doc = ...;
XPath xpath = XPathFactory.newInstance().newXPath();
XPathExpression expr = xpath.compile("/root/items/item1/*/text()");
Object o = expr.evaluate(doc, XPathConstants.NODESET);
NodeList list = (NodeList) o;
import org.w3c.dom.*;
import javax.xml.parsers.*;
import javax.xml.xpath.*;
/**
* File: Ex1.java #author ronda
*/
public class Ex1 {
public static void main(String[] args) throws Exception {
DocumentBuilderFactory Factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = Factory.newDocumentBuilder();
Document doc = builder.parse("myxml.xml");
//creating an XPathFactory:
XPathFactory factory = XPathFactory.newInstance();
//using this factory to create an XPath object:
XPath xpath = factory.newXPath();
// XPath Query for showing all nodes value
XPathExpression expr = xpath.compile("//" + "item1" + "/*");
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());
// seach for the Text children
if (el.getFirstChild().getNodeType() == Node.TEXT_NODE)
System.out.println("inner value:" + el.getFirstChild().getNodeValue());
NodeList children = el.getChildNodes();
for (int k = 0; k < children.getLength(); k++) {
Node child = children.item(k);
if (child.getNodeType() != Node.TEXT_NODE) {
System.out.println("child tag: " + child.getNodeName());
if (child.getFirstChild().getNodeType() == Node.TEXT_NODE)
System.out.println("inner child value:" + child.getFirstChild().getNodeValue());;
}
}
}
}
}
I get this output loading the xml of your question in file named: myxml.xml:
run:
2
tag: tag1
inner value:1
tag: sub
inner value:
child tag: sub1
inner child value:10
child tag: sub2
inner child value:20
...a bit wordy, but allow us to understand how it works. PS: I found a good guide in here
Here is my code, maybe you will notice right away what I'm missing :
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse(fileName));
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPathExpression expr = xpath.compile("//CustomerId");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
Nodelist nodes = (NodeList) result;
Text a = doc.createTextNode("value");
Element p = doc.createElement("newNode");
p.appendChild(a);
for (int i = 0; i < nodes.getLength(); i++) {
nodes.item(i).insertBefore(p, nodes.item(i));
}
I'm trying to insert new node(<newNode>value</newNode>) before CustomerId existing node. Here is my XML sample file :
<Customer>
<names>
<firstName>fName</firstName>
<lastName>lName</lastName>
<middleName>nName</middleName>
<nickName/>
</names>
<addressList>
<address>
<streetInfo>
<houseNumber>22</houseNumber>
<baseName>Street base name</baseName>
<district>kewl district</district>
</streetInfo>
<zipcode>22231</zipcode>
<state>xxx</state>
<country>xxxz</country>
<primary>true</primary>
</address>
</addressList>
<CustomerId/>
<SSN>561381</SSN>
<phone>
<homePhone>123123123</homePhone>
<officePhone/>
<homePhone>21319414</homePhone>
</phone>
<preferred>true</preferred>
</Customer>
This is an exception getting thrown I just don't know what else to try :
NOT_FOUND_ERR: An attempt is made to
reference a node in a context where it
does not exist.
Here an example I just tested using the xml sample you provided.
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setIgnoringComments(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse(new File("XmlTest.xml"));
NodeList nodes = doc.getElementsByTagName("CustomerId");
Text a = doc.createTextNode("value");
Element p = doc.createElement("newNode");
p.appendChild(a);
nodes.item(0).getParentNode().insertBefore(p, nodes.item(0));
Here is the result:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Customer>
<names>
<firstName>fName</firstName>
<lastName>lName</lastName>
<middleName>nName</middleName>
<nickName/>
</names>
<addressList>
<address>
<streetInfo>
<houseNumber>22</houseNumber>
<baseName>Street base name</baseName>
<district>kewl district</district>
</streetInfo>
<zipcode>22231</zipcode>
<state>xxx</state>
<country>xxxz</country>
<primary>true</primary>
</address>
</addressList>
<newNode>value</newNode>
<CustomerId/>
<SSN>561381</SSN>
<phone>
<homePhone>123123123</homePhone>
<officePhone/>
<homePhone>21319414</homePhone>
</phone>
<preferred>true</preferred>
</Customer>
If you're interested, here's the sample code I used to show the result:
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(doc);
transformer.transform(source, result);
String xmlOutput = result.getWriter().toString();
System.out.println(xmlOutput);
I think you want to insert into the parent, not the child:
nodes.item(i).getParentNode().insertBefore(p, nodes.item(i));