How to parse SOAP response using Java API - java

I'm executing the SOAP web services using Karate API and its generating SOAP-based response.
Then I write the response into an XML file using Java Code - File has been created successfully.
Here's the sample SOAP Response Content which I have...
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns1:getMbrWksMembershipDetailsResponse xmlns:ns1="http://www.xxxxxxx.com/services/customersummary">
<ns4:WksMembershipSummaryResponse xmlns:ns2="http://www.xxxxxxx.com/services/customersummary/contract" xmlns:ns3="http://www.xxxxx.com/eom" xmlns:ns4="http://www.xxxx.com/services/customersummary">
<ns2:customerSummary>
<ns2:address>
<ns2:city>SOUTH CHESTERFIELD</ns2:city>
<ns2:country>USA</ns2:country>
<ns2:isoCountryCode>US</ns2:isoCountryCode>
<ns2:line1>9998, N. MICHIGAN ROAD.</ns2:line1>
<ns2:postalCode>23834</ns2:postalCode>
<ns2:state>VA</ns2:state>
</ns2:address>
<ns2:allowPasswordChange>true</ns2:allowPasswordChange>
<ns2:arpMember>false</ns2:arpMember>
<ns2:brandCode>RCI</ns2:brandCode>
<ns2:brandId>1</ns2:brandId>
<ns2:companyCode>RCI</ns2:companyCode>
..........
I have written a java code for fetching the tag value e.g. firstName. This is Java method.
public static String getTagValue(String strResponseFile, String strTagName)
{
String fileLoc = "D:\\getMembershipDetailsResponse.xml";
try
{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(false);
DocumentBuilder db = dbf.newDocumentBuilder();
**Document doc = db.parse(new InputSource(new FileReader(fileLoc)));**
XPath xpath = XPathFactory.newInstance().newXPath();
Node n1 = (Node)xpath.evaluate("//customerSummary/brandCode", doc.getDocumentElement(), XPathConstants.NODE);
System.out.println(n1.getNodeValue());
Node c1 = (Node)xpath.evaluate("//customerSummary/firstName",doc.getDocumentElement(), XPathConstants.NODE);
System.out.println(c1.getNodeValue());
}
catch(Exception e)
{
}
return "";
}
I'm getting NULL in the above bold line of Java code. Can you please give your valuable suggestion on that to resolve the issue?
Thanks,

Related

XPath query in Java is not returning any element

I'm working on a webservice that uses SOAP as a communication protocol: I achieve this by using Spring Boot and Spring WS.
Sometimes, it may happen that I need to send some stuff through the request's header and I want to be able to recover that information via an XPath expression.
Here's what I did so far:
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
WebServiceMessage message = messageContext.getRequest();
Source s = messageContext.getRequest().getPayloadSource();
messageContext.getRequest().writeTo(buffer);
String payload = buffer.toString(java.nio.charset.StandardCharsets.UTF_8.name());
System.out.println(payload);
InputStream is = new ByteArrayInputStream(payload.getBytes());
In the following code snippet I read the whole request that get back-end receives. Afterwards, what I used to do was to transform all of this in a SOAPHeader object, that held everything that I wanted.... except for the fact that I couldn't make any XPath query at all and the element I needed where really down the tree.
So, I decided to continue that snippet of code in the following way
//-------
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new java.io.ByteArrayInputStream(payload.getBytes()));
XPath xpath = XPathFactory.newInstance().newXPath();
Now, this is a sample of the request I'm sending over
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:it="it.niuma.mscsoapws.ws">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-3967AEB46D733EF6E2154990461080350">
<wsse:Username>TAVI</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">pUn8VjdpVaIamSAIwXEeXg==</wsse:Nonce>
<wsu:Created>2019-02-11T17:03:30.803Z</wsu:Created>
</wsse:UsernameToken></wsse:Security>
</soapenv:Header>
<soapenv:Body>
<it:getPOrderRequest>
<it:poNumber>2197111225-F03292</it:poNumber>
</it:getPOrderRequest>
</soapenv:Body>
</soapenv:Envelope>
What I want to extrapolate is the entire part inside soapenv:Header, so I use the following XPath Expression:
XPathExpression expr = xpath.compile("//*//soapenv:Header");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
for (int i = 0; i < nodes.getLength(); i++) {
Node currentItem = nodes.item(i);
System.out.println("found node -> " + currentItem.getLocalName() + " (namespace: " + currentItem.getNamespaceURI() + ")");
}
Here's my problem: the XPath expression, Java side, doesn't return anything while other tools (like this one) evaluates the expression correctly and return something.
The only XPath expression that gets correctly evaluated in Java is //*, nothing else.
What am I missing?
EDIT: After #Wisthler's suggestion, the XPath is now evaluated but it fails to return something. Attaching a screenshot of what happens now: see how the nodes variable (which should contain a NodesList) is, if I understood correctly, empty
I know last time I played with XPath in java I had troubles with namespaces and had to add a namespaceContext to make it work.
you need to "register" the soapenv namespace if you want to use it in the XPath.
You can find below what I did back then. Probably not 100% clean but might still help you
xpath.setNamespaceContext(new NamespaceContext() {
#Override
public String getNamespaceURI(String prefix) {
if("soapenv".equals(prefix)){
return "http://schemas.xmlsoap.org/soap/envelope/";
}else{
return null;
}
}
#Override
public String getPrefix(String namespaceURI) {
return null;
}
#Override
public Iterator getPrefixes(String namespaceURI) {
return null;
}
});
EDIT: made a test run with my fix on your code, is it what you were expecting to see?
found node -> Header (namespace: http://schemas.xmlsoap.org/soap/envelope/)
In addition to #Wisthler's answer, this was the only line of code that was missing for making everything work
factory.setNamespaceAware(true);
So the whole working code is
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new java.io.ByteArrayInputStream(payload.getBytes()));
XPath xpath = XPathFactory.newInstance().newXPath();
xpath.setNamespaceContext(new NamespaceContext() {
#Override
public String getNamespaceURI(String prefix) {
if("soapenv".equals(prefix)){
return "http://schemas.xmlsoap.org/soap/envelope/";
}else{
return null;
}
}
#Override
public String getPrefix(String namespaceURI) {
return null;
}
#Override
public Iterator getPrefixes(String namespaceURI) {
return null;
}
});
XPathExpression expr = xpath.compile("//soapenv:Envelope//soapenv:Header//text()[normalize-space()]");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
Thank you #Wisthler!

Parsing a SOAP response is returning null

String response = "<?xml version='1.0'?><soap:Envelope xmlns:soap='http://www.w3.org/2003/05/soap-envelope'><soap:Body><exch:Response xmlns:exch='http://applicant.ffe.org/exchange/1.0'>...</exch:Response></soap:Body></soap:Envelope>";
DocumentBuilderFactory dbf = null;
DocumentBuilder db = null;
org.w3c.dom.Document document = null;
try {
dbf = DocumentBuilderFactory.newInstance();
db = dbf.newDocumentBuilder();
InputSource is = new InputSource(new ByteArrayInputStream(response.getBytes("UTF-8")));
document = db.parse(is);
} catch(ParserConfigurationException e){}
catch(SAXException e){}
document is returning with null. I have tried different ways to pass to InputSource, but document is still returning null. Any idea why this might be happening?
I just tried i could get the elements name and values .
try {
dbf = DocumentBuilderFactory.newInstance();
db = dbf.newDocumentBuilder();
InputSource is = new InputSource(new ByteArrayInputStream(response.getBytes("UTF-8")));
document = db.parse(is);
System.out.println(document);//here we get null;
System.out.println(document.getNodeName());//here we get document;
for(int i =0 ; i<document.getChildNodes().getLength();i++)
System.out.println(document.getChildNodes().item(i).getChildNodes().item(i).getNodeName());
}
Output :
[#document: null]
document
soap:Body
To parse SOAPResponse we can javax.xml.soap.* it may take u to traverse the object xml tree. Anyway we may need parse the elements from SOAP Body . we could parse these very simple manner using DOM format .

Parsing CDATA content from webservice response in Java

I'm making a webservice call in my Java code and getting the response as String with the below data.
<DocumentElement>
<ResultSet>
<Response>Successfully Sent.</Response>
<UsedCredits>1</UsedCredits>
</ResultSet>
</DocumentElement>
Now, I want to parse the below response String and get the value of <Response> tag alone for further processing. Since, I'm getting only the CDATA content how to parse the String content?
You obviously need XML parser for this one. Lets say the above is stored in a String variable named content
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
//Using factory get an instance of document builder
DocumentBuilder db = dbf.newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(content)); //content variable holding xml records
Document doc = db.parse(is)
/* Now retrieve data from xml */
NodeList nodes = doc.getElementsByTagName("ResultSet");
Element elm = (Element) nodes.item(0); //will get you <Response> Successfully Sent.</Response>
String responseText = elm.getFirstChild().getNodeValue();
}catch(ParserConfigurationException pce) {
pce.printStackTrace();
}catch(SAXException se) {
se.printStackTrace();
}catch(IOException ioe) {
ioe.printStackTrace();
}
Follow the above parser routine for larger data sets. Use loops for multiple similar data sets. Hope that helps.

Java Web Service returns string with > and < instead of > and <

I have a java web service that returns a string. I'm creating the body of this xml string with a DocumentBuilder and Document class. When I view source of the returned XML (Which looks fine in the browser window) instead of <> it's returning < and > around the XML nodes.
Please help.
****UPDATE (including code example)
The code is not including any error catching, it was stripped for simplicity.
One code block and three methods are included:
The first code block (EXAMPLE SETUP) shows the basics of what the Document object is setup to be like. the method appendPayment(...) is where the actual document building happens. It calls on two helper methods getTagValue(...) and prepareElement(...)
**Note, this code is meant to copy specific parts from a pre-existing xml string, xmlString, and grap the necessary information to be returned later.
****UPDATE 2
Response added at the end of the question
************ Follow-up question to the first answer is here:
How to return arbitrary XML Document using an Eclipse/AXIS2 POJO Service
EXAMPLE SETUP
{
//create new document
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder newDocBuilder = docFactory.newDocumentBuilder();
Document newDoc = newDocBuilder.newDocument();
Element rootElement = newDoc.createElement("AllTransactions");
newDoc.appendChild(rootElement);
appendPayment(stringXML, newDoc);
}
public static void appendPayment(String xmlString, Document newDoc) throws Exception
{
//convert string to inputstream
ByteArrayInputStream bais = new ByteArrayInputStream(xmlString.getBytes());
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document oldDoc = docBuilder.parse(bais);
oldDoc.getDocumentElement().normalize();
NodeList nList = oldDoc.getChildNodes();
Node nNode = nList.item(0);
Element eElement = (Element) nNode;
//Create new child node for this payment
Element transaction = newDoc.createElement("Transaction");
newDoc.getDocumentElement().appendChild(transaction);
//status
transaction.appendChild(prepareElement("status", eElement, newDoc));
//amount
transaction.appendChild(prepareElement("amount", eElement, newDoc));
}
private static String getTagValue(String sTag, Element eElement)
{
NodeList nlList = eElement.getElementsByTagName(sTag).item(0).getChildNodes();
Node nValue = (Node) nlList.item(0);
return nValue.getNodeValue();
}
private static Element prepareElement(String sTag, Element eElement, Document newDoc)
{
String str = getTagValue(sTag, eElement);
Element newElement = newDoc.createElement(sTag);
newElement.appendChild(newDoc.createTextNode(str));
return newElement;
}
Finally, I use the following method to convert the final Document object to a String
public static String getStringFromDocument(Document doc)
{
try
{
DOMSource domSource = new DOMSource(doc);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(domSource, result);
return writer.toString();
}
catch(TransformerException ex)
{
ex.printStackTrace();
return null;
}
}
The header type of the response is as follows
Server: Apache-Coyote/1.1
Content-Type: text/xml;charset=utf-8
Transfer-Encoding: chunked
This is an example response
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<getTransactionsResponse xmlns="http://services.paypal.com">
<getTransactionsReturn><AllTransactions><Transaction><status>PENDING</status><amount>55.55</amount></transaction>
</getTransactionsResponse>
</soapenv:Body>
</soapenv:Envelope>
The framework is doing what you tell it; your method returns a String which means the generated WSDL should have a response message of type <xsd:string>. As we know, XML strings must encode certain characters as character entity references (i.e. "<" becomes "<" so the XML parser treats it as a string, not the beginning of an XML element as you intend). If you want to return an XML document then you must define the XML structure in the WSDL <types> section and set the response message part to the appropriate element.
To put it another way, you are trying to send "typed" data without using the strong type system provided by SOAP/WSDL (namely XML schema); this is generally regarded as bad design (see Loosely typed versus strongly typed web services).
The ultimate solution is to to define the response document via a proper XML Schema. If there is no set schema, as by the design of your service, then use the <xsd:any> type for the message response type, although this approach has its pitfalls. Moreover, such a redesign implies a schema-first (top-down) development model and from the comment stream it seems that you are currently practicing code-first (bottom-up) approach. Perhaps your tools provide a mechanism such as a "general XML document" return type or annotation which achieves the same effect.

Using a string inside the DocumentBuilder parse method (need it for parsing XML using XPath) [duplicate]

This question already has answers here:
In Java, how do I parse XML as a String instead of a file?
(6 answers)
Closed 9 years ago.
I'm trying to create a RESTful webservice using a Java Servlet. The problem is I have to pass via POST method to a webserver a request. The content of this request is not a parameter but the body itself.
So I basically send from ruby something like this:
url = URI.parse(#host)
req = Net::HTTP::Post.new('/WebService/WebServiceServlet')
req['Content-Type'] = "text/xml"
# req.basic_auth 'account', 'password'
req.body = data
response = Net::HTTP.start(url.host, url.port){ |http| puts http.request(req).body }
Then I have to retrieve the body of this request in my servlet. I use the classic readline, so I have a string. The problem is when I have to parse it as XML:
private void useXML( final String soft, final PrintWriter out) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException, FileNotFoundException {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true); // never forget this!
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse(soft);
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPathExpression expr = xpath.compile("//software/text()");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
for (int i = 0; i < nodes.getLength(); i++) {
out.println(nodes.item(i).getNodeValue());
}
}
The problem is that builder.parse() accepts: parse(File f), parse(InputSource is), parse(InputStream is).
Is there any way I can transform my xml string in an InputSource or something like that? I know it could be a dummy question but Java is not my thing, I'm forced to use it and I'm not very skilled.
You can create an InputSource from a string by way of a StringReader:
Document doc = builder.parse(new InputSource(new StringReader(soft)));
With your string, use something like :
ByteArrayInputStream input =
new ByteArrayInputStream(yourString.getBytes(perhapsEncoding));
builder.parse(input);
ByteArrayInputStream is an InputStream.

Categories

Resources