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();
}
Related
that is my xml
<?xml version = "1.0" encoding = "UTF-8"?>
<ns0:GetADSLProfileResponse xmlns:ns0 = "http://">
<ns0:Result>
<ns0:eCode>0</ns0:eCode>
<ns0:eDesc>Success</ns0:eDesc>
</ns0:Result>
</ns0:GetADSLProfileResponse>
that is my code in java I need to know how to start in this
I tried some code online but still did not solve my problem
how to get the values in the result to loop in it and get 0 in ecode and Success in eDesc
CustomerProfileResult pojo = new CustomerProfileResult();
String body = readfile();
System.out.println(body);
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document dom = db.parse(new InputSource(new StringReader(body)));
XPath xpath =XPathFactory.newInstance().newXPath();
XPathExpression name = xpath.compile("/xml/GetADSLProfileResponse/Result");
NodeList nodeName = (NodeList) name.evaluate(dom, XPathConstants.NODESET);
if(nodeName!=null){
}
Summary
You can try to following expression which allows you to select nodes without caring the namespace ns0:
/*[local-name()='GetADSLProfileResponse']/*[local-name()='Result']/*
Explanation
In your syntax, several parts were incorrect. Let's take a look together. XPath syntax /xml means that the root node of the document is <xml>, but the root element is <ns0:GetADSLProfileResponse>; GetADSLProfileResponse is incorrect too, because your XML file contains a namespace. Same for Result:
/xml/GetADSLProfileResponse/Result
In my solution, I ignored the namespace, because your namespace provided is incomplet. Here's a full program to get started:
String XML =
"<?xml version = \"1.0\" encoding = \"UTF-8\"?>\n"
+ "<ns0:GetADSLProfileResponse xmlns:ns0 = \"http://\">\n"
+ " <ns0:Result>\n"
+ " <ns0:eCode>0</ns0:eCode>\n"
+ " <ns0:eDesc>Success</ns0:eDesc>\n"
+ " </ns0:Result>\n"
+ "</ns0:GetADSLProfileResponse> ";
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document;
try (InputStream in = new ByteArrayInputStream(XML.getBytes(StandardCharsets.UTF_8))) {
document = builder.parse(in);
}
XPath xPath = XPathFactory.newInstance().newXPath();
XPathExpression expr = xPath.compile("/*[local-name()='GetADSLProfileResponse']/*[local-name()='Result']/*");
NodeList nodeList = (NodeList) expr.evaluate(document, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
System.out.println(node.getNodeName() + ": " + node.getTextContent());
}
It prints:
ns0:eCode: 0
ns0:eDesc: Success
See also:
How to query XML using namespaces in Java with XPath?
Node (Java Platform SE 8)
Collogues, i have cycle which create soap xml with nessesary structure (don't ask about the structure)
log.info("Body elements: ");
NodeList nodeList = body.getElementsByTagName("*") ;
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
log.info(node.getNodeName());
if (node.getNodeName().equals("ns2:request")) {
log.info("Set namespace and prefix for " + node.getNodeName());
SOAPElement childX = (SOAPElement) node;
childX.removeNamespaceDeclaration(childX.getPrefix()) ;
childX.addNamespaceDeclaration("ns3", "http://mayacomp/Generic/Ws");
childX.setPrefix("ns3");
}
else {
if (node.getNodeName().equals("ns2:in") ) {
log.info("Remove namespace for " + node.getNodeName());
SOAPElement childX = (SOAPElement) node;
childX.removeNamespaceDeclaration(childX.getPrefix()) ;
childX.addNamespaceDeclaration("", "");
childX.setPrefix("");
}
SOAPElement childX = (SOAPElement) node;
childX.removeNamespaceDeclaration(childX.getPrefix()) ;
childX.setPrefix("");
}
}
}
As a result I receive xml:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns3:request xmlns:ns3="http://mayacomp/Generic/Ws">
<in xmlns="http://mayacomp/Generic/Ws">
<requestHeader>
My question is how to remove only xmlns="http://mayacomp/Generic/Ws" from <in> element and receive:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns3:request xmlns:ns3="http://mayacomp/Generic/Ws">
<in>
<requestHeader>
UPDATE
I tried to config xml element:
/*Config body elements*/
while (itBodyElements.hasNext())
{
Object o = itBodyElements.next();
SOAPBodyElement bodyElement = (SOAPBodyElement) o;
log.info("Elements from 'Body' element = " + bodyElement.getLocalName() );
Iterator it2 = bodyElement.getChildElements();
while (it2.hasNext())
{
Object requestElement = it2.next();
SOAPBodyElement bodyRequest = (SOAPBodyElement) requestElement;
log.info(" Elements from '"+ bodyElement.getLocalName() + "' element = " + bodyRequest.getLocalName());
log.info(" Delete namespace from IN element " + bodyRequest.getLocalName());
bodyRequest.removeNamespaceDeclaration(bodyRequest.getPrefix());
bodyRequest.setPrefix("");
Iterator it3 = bodyRequest.getChildElements();
while (it3.hasNext())
{ //work with other elements
But it has not effect to 'in' element. After run i still have:
<in xmlns="http://mayacomp/Generic/Ws">
UPDATE
I solved the problem by calling ws as next:
getWebServiceTemplate().marshalSendAndReceive(
"URL",
request,
new WebServiceMessageCallback()
{ public void doWithMessage(WebServiceMessage message) {
SaajSoapMessage saajSoapMessage = (SaajSoapMessage)message;
SOAPMessage soapMessage = UtilsClass.createSOAPMessage(in);
saajSoapMessage.setSaajMessage(soapMessage);
}
}
);
Method createSOAPMessage configure soap message using javax.xml.soap library.
If I understand the question correctly, your code gets the following XML as input:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns2:request xmlns:ns3="http://mayacomp/Generic/Ws">
<ns2:in>
<ns2:requestHeader>
And you want to transform it into the following XML:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns3:request xmlns:ns3="http://mayacomp/Generic/Ws">
<in>
<requestHeader>
In this case, adjusting the namespace declarations and namespace prefixes is not enough because in the DOM the in and requestHeader elements will still be in the http://mayacomp/Generic/Ws namespace. That is because the namespace URI of a DOM element is determined at creation/parse time and doesn't change when namespace declarations are added or removed later. When the DOM is serialized, the serializer will automatically generate the necessary namespace declarations to make sure that the elements in the output effectively have the namespaces they had in the DOM. That is why you get xmlns="http://mayacomp/Generic/Ws" in the output, although that namespace declaration is not present in the DOM.
What you really need to do is to change the namespace URI for these elements. Unfortunately, DOM nodes don't have a setNamespaceURI method and you need to use Document.renameNode instead.
You could just remove the attribute using something like
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nList = (NodeList)xPath.evaluate("/Envelope/Body/request/in", body, XPathConstants.NODESET);
for (int i = 0; i < nList.getLength(); ++i) {
Element e = (Element) nList.item(i);
e.removeAttribute("xmlns");
}
The following test shows that it does work.
#Test
public void removeXmlns() throws Exception {
String xml = "" +
"<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +
" <soapenv:Body>\n" +
" <ns3:request xmlns:ns3=\"http://mayacomp/Generic/Ws\">\n" +
" <in xmlns=\"http://mayacomp/Generic/Ws\">\n" +
" <requestHeader>\n" +
" </requestHeader>\n" +
" </in>\n" +
" </ns3:request>\n" +
" </soapenv:Body>\n" +
"</soapenv:Envelope>";
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(ResourceUtils.getFile("/soaptest.xml").getAbsolutePath());
Element body = document.getDocumentElement();
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nList = (NodeList)xPath.evaluate("/Envelope/Body/request/in", body, XPathConstants.NODESET);
for (int i = 0; i < nList.getLength(); ++i) {
Element e = (Element) nList.item(i);
e.removeAttribute("xmlns");
}
DOMSource domSource = new DOMSource(document);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(domSource, result);
logger.info("XML IN String format is: \n" + writer.toString());
}
The output is
2015-11-26-11-46-24[]::[main]:(demo.TestCode.removeXmlns(TestCode.java:174):174):INFO :TestCode:XML IN String format is:
<?xml version="1.0" encoding="UTF-8" standalone="no"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns3:request xmlns:ns3="http://dfg/Ws">
<in>
<requestHeader>
</requestHeader>
</in>
</ns3:request>
</soapenv:Body>
</soapenv:Envelope>
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.
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();
}
}
This question already has answers here:
Get element name from XML in Java DOM
(3 answers)
Closed 8 years ago.
I never really know how to work with XML tags.How do I traverse the node and print particular node in the XML tag.Below is the XML file.
<Employees>
<Employee>
<Gender></Gender>
<Name>
<Firstname></Firstname>
<Lastname></Lastname>
</Name>
<Email></Email>
<Projects>
<Project></Project>
</Projects>
<PhoneNumbers>
<Home></Home>
<Office></Office>
</PhoneNumbers>
</Employee>
There is no data but this is the structure.I am using the following code to parse it partially.
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
DocumentBuilder builder = factory.newDocumentBuilder();
Document xmlDocument = builder.parse("employees.xml");
System.out.println(xmlDocument.getDocumentElement().getNodeName());
I would like to print the gender and lastname values.How do I parse the tag which is inside the Name tag which in turn the Name is inside the Employee tag.
Regards.
You should use XPATH. There is a good explanation in this post.
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(<uri_as_string>);
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
XPathExpression expr = xpath.compile(<xpath_expression>);
Try this.
String expression = "/Employees/Employee/Gender"; //read Gender value
NodeList nList = (NodeList) xPath.compile(expression).evaluate(document, XPathConstants.NODESET);
for (int j = 0; nList != null && j < nList.getLength(); j++) {
Node node = nList.item(j);
System.out.println("" + node.getFirstChild().getNodeValue());
}
expression = "/Employees/Employee/Name/Lastname"; //read Lastname value
nList = (NodeList) xPath.compile(expression).evaluate(document, XPathConstants.NODESET);
for (int j = 0; nList != null && j < nList.getLength(); j++) {
Node node = nList.item(j);
System.out.println("" + node.getFirstChild().getNodeValue());
}