Generating XPATH Query issue - java

I have a SOAP Response as below
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<NTService.DisconnectResourceResult xmlns="http://www.evolving.com/NumeriTrack/xsd">
<retData xmlns="">
<retCode>rcSuccess</retCode>
<retMsg/>
<resErrList/>
</retData>
</NTService.DisconnectResourceResult>
</soapenv:Body>
</soapenv:Envelope>
Am not good in generating XPATH query, but using SOAPUI I was able to fetch the XPATH query to fetch retCode as below :
//ns1:NTService.DisconnectResourceResult[1]/retData[1]/retCode[1]/text()
In Java, am trying to fetch retCode but am unable to fetch the output.
XPath xpath = XPathFactory.newInstance().newXPath();
xpath.setNamespaceContext(new SoapNamespaceContext());
String sxpath="//ns1:NTService.DisconnectResourceResult[1]/retData[1]/retCode[1]/text()";
System.out.println("sxpath is " + sxpath);
XPathExpression expr;
expr = xpath.compile(sxpath);
System.out.println("expr is " + expr);
Object result;
result = expr.evaluate(sb, XPathConstants.NODESET);
System.out.println("result is " + result);
NodeList nodes = (NodeList) result;
System.out.println("Length of result is " + nodes.getLength());
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i).getNodeValue());
}
I have set the namespace as well.
public String getNamespaceURI(String prefix) {
System.out.println("Prefix is " +prefix);
if (prefix == null) throw new NullPointerException("Null prefix");
else if ("ns1".equals(prefix)) {
System.out.println("Returning http://www.evolving.com/NumeriTrack/xsd");
return "http://www.evolving.com/NumeriTrack/xsd";
}
else if ("n".equals(prefix)) {
System.out.println("Returning http://schemas.xmlsoap.org/soap/envelope/");
return "http://schemas.xmlsoap.org/soap/envelope/";
}
else if ("xml".equals(prefix)) return XMLConstants.XML_NS_URI;
return XMLConstants.NULL_NS_URI;
}
`
Can anyone please suggest me to get retCode from the SOAP Message response.
Thanks

Here's the anserw :
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File("D:\\Loic_Workspace\\Test2\\res\\test.xml"));
System.out.println(doc.getElementsByTagName("retCode").item(0).getTextContent());
You should use .getTextContent() method.
Will output rcSuccess as you want :)
Hope it's helps,

Related

Parsing a SOAP response using XPath Java

I am new to XPath. I have the following SOAP response:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<addParentResponse xmlns="urn:JadeWebServices/NetsuiteCustomer/">
<addParentResult>Organisation xxxxx already exists - use UpdateParent method instead</addParentResult>
</addParentResponse>
</soap:Body>
</soap:Envelope>
Can anyone kindly give me some code which will read the value of "addParentResult"?
Regards,
Anirban.
The following xpath should give the desired result :
/soap:Envelope/soap:Body/parentns:addParentResponse/parentns:addParentResult/text()
The reason I added parentns to xpath is that your xml has namespaces and your xpath processor should know about them. But the addParentResponse has no prefix and has default namespace. In this case add a prefix in xpath expression and before doing that tell xpath processor that for the parentns prefix there is a value which is "urn:JadeWebServices/NetsuiteCustomer/". It is done via a NamespaceContext.
Also be sure to tell the DocumentBuilderFactory that it should be aware of namespaces by using setNamespaceAware( true );
Code in Java would be :
try
{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse( new File( "soapResponse.xml" ) );
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xpath = xPathFactory.newXPath();
javax.xml.namespace.NamespaceContext ns = new javax.xml.namespace.NamespaceContext()
{
#Override
public String getNamespaceURI(String prefix)
{
if ( "soap".equals( prefix ) )
{
return "http://schemas.xmlsoap.org/soap/envelope/";
}
else if ( "xsi".equals( prefix ) )
{
return "http://www.w3.org/2001/XMLSchema-instance";
}
else if ( "xsd".equals( prefix ) )
{
return "http://www.w3.org/2001/XMLSchema";
}
else if ( "xml".equals( prefix ) )
{
return javax.xml.XMLConstants.XML_NS_URI;
}
else if ( "parentns".equals( prefix ) )
{
return "urn:JadeWebServices/NetsuiteCustomer/";
}
return javax.xml.XMLConstants.NULL_NS_URI;
}
#Override
public String getPrefix(String namespaceURI)
{
return null;
}
#Override
public Iterator<?> getPrefixes(String namespaceURI)
{
return null;
}
};
xpath.setNamespaceContext(ns);
XPathExpression expr = xpath.compile( "/soap:Envelope/soap:Body/parentns:addParentResponse/parentns:addParentResult/text()" );
Object exprEval = expr.evaluate( doc, XPathConstants.STRING );
if ( exprEval != null )
{
System.out.println( "The text of addParentResult is : " + exprEval );
}
}
catch ( Exception e )
{
e.printStackTrace();
}
}
To test this code, put your xml in a file called soapResponse.xml at the same level as your java file.
Output from System.out.println() is :
The text of addParentResult is : Organisation xxxxx already exists - use UpdateParent method instead

How to remove prefix and namespace in SOAPElement?

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>

xpath parsing multiple values in a single call

How do you get xPath value for more than one path in a single call.
for example
<Message version="010" release="006" xmlns="http://www.ncpdp.org/schema/SCRIPT" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Header>
</Header>
<Body>
<CommunicationNumbers>
<Communication>
<Number>5551234444</Number>
<Qualifier>TE</Qualifier>
</Communication>
<Communication>
<Number>5551235555</Number>
<Qualifier>FX</Qualifier>
</Communication>
</CommunicationNumbers>
<Identification>
<FileID>616</FileID>
<DEANumber>AB123456</DEANumber>
<SocialSecurity>123456789</SocialSecurity>
</Identification>
<Specialty>A</Specialty>
<ClinicName>Therapy Department</ClinicName>
<Name>
<LastName>Xavior</LastName>
<FirstName>Charles</FirstName>
<MiddleName>C</MiddleName>
<Suffix>MD</Suffix>
</Name>
<Address>
<AddressLine1>888 ABC Drive</AddressLine1>
<AddressLine2>Suite 200</AddressLine2>
<City>Miami</City>
<State>FL</State>
<ZipCode>12345</ZipCode>
</Address>
</Body>
and I need values for :
:Communication/Number
:Identification/FileID
:Specialty
in a single call.
For a single value I am using
public static String getExpValue(final String xmlString, final String expression, final ServiceNamespaceContext nameSpace) throws XPathExpressionException {
StringReader strReader = new StringReader(xmlString);
InputSource inputStr = new InputSource(strReader);
String result = null;
try {
final XPath xpath = XPathFactory.newInstance().newXPath();
xpath.setNamespaceContext(nameSpace);
final XPathExpression expr = xpath.compile(expression);
result = (String) expr.evaluate(inputStr, XPathConstants.STRING);
} finally {
strReader = null;
inputStr = null;
}
return result;
}
My desired output is a single concatenated String 5551234444616A
You could try using something like...
XPathExpression expr = xpath.compile("//Communication/Number | //Identification/FileID");
Which should combine the results of each query. In my (simply) test, I got 3 matches (2 for Communication/Number and 1 for Identification/FileID)
Updated
The intended result was to return a NodeList, for example...
NodeList nl = (NodeList)expr.evaluate(inputStr, XPathConstants.NODELIST);
for (int index = 0; index < nl.getLength(); index++) {
Node node = nl.item(index);
String value = node.getTextContent();
System.out.println(value);
}
Since Java 7 the NodeList is replaced by NodeSet:
NodeList nl = (NodeList)expr.evaluate(inputStr, XPathConstants.NODESET);
for (int index = 0; index < nl.getLength(); index++) {
Node node = nl.item(index);
String value = node.getTextContent();
System.out.println(value);
}

Get Soap Body using XPath and XML XPath Api

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();
}

Android :Parse XML Data: Tags with arguments

I want to parse an XML file i exported from a database software, for my android app.
However some of the tags have arguments like so:
<row>
<value column="Index" null="false">1</value>
<value column="Front" null="false">INFO</value>
<value column="Back" null="false">INFO</value>
<value column="Check" null="false">0</value>
</row>
what string value do i specify while trying to find the start tag to parse it?
( for example: to find the row i compare the start tap to "row" and if it returns true i compute the data. What do i do for each value i.e Index,Front,Back and Check separately?)
My java code is as follows
try{
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
InputStream stream = context.getResources().openRawResource(com.Whydea.chemistryhelper.R.raw.appxml);
xpp.setInput(stream, null);
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT){
if(eventType==XmlPullParser.START_TAG){
handleStartTag(xpp.getName()); //handels Start Tag
} else if (eventType==XmlPullParser.END_TAG){
handleEndTag(xpp.getName());
Ctag=null;
} else if (eventType==XmlPullParser.TEXT){
handleText(xpp.getText());
}
eventType=xpp.next();
}
}catch (NotFoundException e){
Log.d("XMLpp",e.getMessage());
}catch (XmlPullParserException e){
Log.d("XMLpp",e.getMessage());
}catch (IOException e){
Log.d("XMLpp",e.getMessage());
}
`
EDIT:
Each "value" start tags has its own attribute(column=...), how do i access those?
For example: to access a row, i have a String constant with the value "row", and check if the start tag corresponds to that, and it works. But when i declare a string constant with value "value column=\"Check\" null=\"false\""( i have to use \ other wise " give errors), it does not find find that start tag. So what should my constant be?
if i understand your question correctly then to get each of the values you need to do following, basically you want to get the value of each attribute inside the xml tag
int attributeCount = xpp.getAttributeCount();
for(int i = 0; i<attributeCount; i++){
String name = xpp.getAttributeName(i);
//Log.d(TAG, "Name: "+name);
if(name != null && name.equalsIgnoreCase("column")){
return Integer.parseInt(xpp.getAttributeValue(i));
}
}
So once you have encountered the row then you look for the start Tag "value" once you have found it, then use the above code to get the individual value of attributes.
As per your comment if you want to get the text value of an XML tag then you will have to use the getText() method. Once you have found the START_TAG value then execute below code:
eventType = xpp.next();
if(eventType == XmlPullParser.TEXT){
String text = xpp.getText();
}
For the xml tag 'INFO' value it will return 'INFO'
try {
final Service S = new Service();
String xmlString = S.ImportAllPollBoothStatus(IMEI,asscd, boothno);
if(xmlString.toLowerCase().trim().equals("false")){
return false;
}
DocumentBuilderFactory docFactory = DocumentBuilderFactory
.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(xmlString));
Document doc = docBuilder.parse(is);
NodeList nodes = doc.getElementsByTagName("HT");
for (int i = 0; i < nodes.getLength(); i++) {
Element element = (Element) nodes.item(i);
NodeList blockidnodes = doc.getElementsByTagName("Table");
for (int blockidcount = 0; blockidcount < blockidnodes
.getLength(); blockidcount++) {
NodeList PollpercentId = element
.getElementsByTagName("PollpercentId");
Element line1 = (Element) PollpercentId.item(blockidcount);
NodeList asscd1 = element
.getElementsByTagName("asscd");
Element line2 = (Element) asscd1.item(blockidcount);
NodeList pollgcd = element
.getElementsByTagName("pollgcd");
Element line3 = (Element) pollgcd.item(blockidcount);
NodeList SessionYearIdref = element
.getElementsByTagName("SessionYearIdref");
Element line4 = (Element) SessionYearIdref.item(blockidcount);
NodeList MaleVoters = element
.getElementsByTagName("MaleVoters");
Element line5 = (Element) MaleVoters.item(blockidcount);
NodeList FemaleVoters = element
.getElementsByTagName("FemaleVoters");
Element line6 = (Element) FemaleVoters.item(blockidcount);
NodeList UpdatedDate = element
.getElementsByTagName("UpdatedDate");
Element line7 = (Element) UpdatedDate.item(blockidcount);
NodeList timeslot = element
.getElementsByTagName("timeslot");
Element line8 = (Element) timeslot.item(blockidcount);
this.db.insertVotingStatus(
getCharacterDataFromElement(line1),
getCharacterDataFromElement(line2),
getCharacterDataFromElement(line3),
getCharacterDataFromElement(line4),
getCharacterDataFromElement(line5),
getCharacterDataFromElement(line6),
getCharacterDataFromElement(line7),
getCharacterDataFromElement(line8));
}
}
}
catch (Exception e) {
Log.e("EXCEPTION DURING VIDHANSABHA INSERION",
"======Insert-VIDHANSABHA-DETAILS=====================" + e);
return false;
}
return true;
Did you develop the app which generates the XML file? If so why don't you change it? It would be much easier to parse the XML if it has this format:
<item Index="1" Front="INFO" Back="INFO" Check="0"/>
<item Index="2" Front="INFO" Back="INFO" Check="1"/>

Categories