Parse xml with namespaces via dom4j XPath strange behaviour - java

The problem is - when I parse an XML with namespaces through XPath it parses XML partly, for example if I set to XPath: /SOAP-ENV:Envelope/SOAP-ENV:Body - parser recognize this good, but if I go further - parser crushes without returning exception.
This is an XML:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:m0="http://schemas.compassplus.com/two/1.0/fimi.xsd"
xmlns:m="http://schemas.compassplus.com/two/1.0/fimi.wsdl">
<SOAP-ENV:Body>
<m:GetAcctInfoRp xmlns:m="http://schemas.compassplus.com/two/1.0/fimi.xsd">
<Response Response="1" Product="FIMI" Ver="0"><m0:Avail>995526.4</m0:Avail>
<m0:Bonus>0</m0:Bonus>
<m0:Branch>1</m0:Branch>
<m0:Cards><m0:Row>
<m0:PAN>6706250002450356</m0:PAN>
<m0:MBR>0</m0:MBR>
<m0:CardUID>E4BFBC24A2844F13BE5C5AEEB15D27CE</m0:CardUID>
<m0:Status>1</m0:Status>
</m0:Row>
<m0:Row>
<m0:PAN>6706255660781224</m0:PAN>
<m0:MBR>0</m0:MBR>
<m0:CardUID>971111C18D774C3BA26434336CB57475</m0:CardUID>
<m0:Status>1</m0:Status>
</m0:Row>
</m0:Cards>
<m0:CreditHold>0</m0:CreditHold>
<m0:Currency>810</m0:Currency>
<m0:DebitHold>50240.81</m0:DebitHold>
<m0:DropTmpOverOnRefresh>0</m0:DropTmpOverOnRefresh>
<m0:FoundAccount>40817810200001058114</m0:FoundAccount>
<m0:FoundAccountUID>E79459BEEEF94BEFBA57D0D23503EF7E</m0:FoundAccountUID>
<m0:LastDepAmount>10</m0:LastDepAmount>
<m0:LastDepTime>2012-03-06T17:23:35</m0:LastDepTime>
<m0:LastRefreshTime>2012-02-22T12:49:47</m0:LastRefreshTime>
<m0:LastTranId>4172</m0:LastTranId>
<m0:LastWdlAmount>100</m0:LastWdlAmount>
<m0:LastWdlTime>2012-03-06T17:29:44</m0:LastWdlTime>
<m0:Ledger>1045767.21</m0:Ledger>
<m0:MaskBalances>0</m0:MaskBalances>
<m0:Overdraft>9000000</m0:Overdraft>
<m0:PersonExtId>1891</m0:PersonExtId>
<m0:PersonFIO>bla bla bla</m0:PersonFIO>
<m0:PersonId>782</m0:PersonId>
<m0:Remain>1046068.21</m0:Remain>
<m0:Status>3</m0:Status>
<m0:TmpOverdraft>0</m0:TmpOverdraft>
<m0:Type>1</m0:Type>
<m0:UserFields><m0:Row>
<m0:Name>test</m0:Name>
<m0:TextValue>ั‚ะตัั‚</m0:TextValue>
</m0:Row>
</m0:UserFields>
</Response>
</m:GetAcctInfoRp>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Java code that should parse this XML file:
String res = RequestMethods.executeXMLPostRequest(url, xml);
Document doc = DocumentHelper.parseText(res.trim());
String session = doc.valueOf("SOAP-ENV:Envelope/SOAP-ENV:Body/m:GetAcctInfoRp/Response/m0:Avail");
PS sorry for my English, may be someone solved this problem yet?
Please help.
THe 1-st issue is solved, another question is - how can I get in cycled structure, for example if We have several SOAP-ENV:Envelope/SOAP-ENV:Body//m0:Avail elements?

This works for me
public static void main(String[] args) throws DocumentException {
String xmlText = getContents(new File("/home/bpgergo/Temp/9682103.xml"));
Document doc = DocumentHelper.parseText(xmlText);
String session = doc.valueOf("SOAP-ENV:Envelope/SOAP-ENV:Body//m0:Avail");
System.out.println("session:"+session);
}

Related

Get SOAP element by tag with namespace in Java

I wondered how it was possible to get the content of a SOAP response body child when the child tag looks like this:
<sth:x>...</sth:x>
In the Oracle docs I found how to loop all Elements with a specific name.
But therefore I need to create an Element first that specifies the name of the tag I want to search.
How do I create an element looking like the one above? I know how to make one like this:
<sth:id sth="asdf">
But that doesn't really work.
Here is the server-response I attempt to read.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header/>
<soapenv:Body>
<ns5:loginResponse xmlns:ns5="x">
<ns5:id>Value I am looking for</ns5:id>
<ns5:rc>0</ns5:rc>
</ns5:loginResponse>
</soapenv:Body>
</soapenv:Envelope>
Thanks for your help :)
Try this:
String xml = "YourXMl";
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource is = new InputSource(new StringReader(xml));
Document doc = builder.parse(is);
NodeList nodes = doc.getDocumentElement().getElementsByTagNameNS("x","id");
System.err.println(nodes.item(0).getChildNodes().item(0).getNodeValue());
There are two important things factory.setNamespaceAware(true); - to turn on support for xml namespaces.
doc.getDocumentElement().getElementsByTagNameNS("x","id"); To get element using namespace uri and element name. And here is <ns5:loginResponse xmlns:ns5="x"> declaration of namespace uri. x is uri ns5 is namespace.
I found the answer:
The Element in question was within another SOAPBodyElement.
So looping through both elements gave me the correct Value:
body = reply.getSOAPBody();
Iterator replyIt = body.getChildElements();
while(replyIt.hasNext()){
SOAPBodyElement replysbe = (SOAPBodyElement) replyIt.next();
Iterator replyIt2 = replysbe.getChildElements();
SOAPElement sentSE = (SOAPElement) replyIt2.next();
sessionID = sentSE.getValue();
}

XPATH won't work

I am trying to extract a 'PartyID' from a request using XPath. This request is in the form of XML.
Here is the XML:
<?xml version="1.0" encoding="UTF-8"?>
<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>
<s1:invokerules xmlns:s1="http://rules.kmtool.abc.com"><s1:arg0><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<kbdInitiateRequest>
<kmTestHeader>
<MessageId>USER1_MSG1</MessageId>
<TestDate>08/07/2008 07:34:15</TestDate>
<TestReference>
<ConductorReference>
<InvokeIdentifier>
<RefNum>USER1_Ref1</RefNum>
</InvokeIdentifier>
</ConductorReference>
</TestReference>
<TestParty>
<ConductorParty>
<Party PartyID="123456789" AgencyID="DUNS">
<TestContact>
<DetailedContact>
<ContactName>Michael Jackson</ContactName>
<Telephone>02071059053</Telephone>
<TelephoneExtension>4777</TelephoneExtension>
<Email>Michal.Jackson#Neverland.com</Email>
<Title>Mr</Title>
<FirstName>Michael</FirstName>
<Initials>MJ</Initials>
</DetailedContact>
</TestContact>
</Party>
</ConductorParty>
<PerformerParty>
<Party PartyID="987654321" AgencyID="DUNS">
</Party>
</PerformerParty>
</TestParty>
</kmTestHeader>
<kmToolMessage>
<controlNode>
<userRequest>INITIATE</userRequest>
</controlNode>
<customer>
<circuitID>000111333777</circuitID>
</customer>
</kmToolMessage>
</kbdInitiateRequest>
]]></s1:arg0>
</s1:invokerules>
</soapenv:Body>
</soapenv:Envelope>
I have a method in my java code called getPartyId(). This method should extract the PartyID from the XML. However I cannot get this method to return the PartyID no matter what XPath query I use, this is where I need help.
Here is the getPartyId method:
private String getPartyId(String xml) throws XPathExpressionException
{
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
xpath.setNamespaceContext(new NamespaceContext() {
public String getNamespaceURI(String prefix) {
if (prefix == null) throw new NullPointerException("Null prefix");
else if ("SOAP-ENV".equals(prefix)) return "http://schemas.xmlsoap.org/soap/envelope/";
else if ("xml".equals(prefix)) return XMLConstants.XML_NS_URI;
return XMLConstants.NULL_NS_URI;
}
public String getPrefix(String uri) {
throw new UnsupportedOperationException();
}
public Iterator getPrefixes(String uri) {
throw new UnsupportedOperationException();
}
});
XPathExpression expr = xpath.compile("/SOAP-ENV:Envelope/SOAP-ENV:Body/*/*/*/*/*/*/*/*/*/*/*[local-name()='PartyID']/text()");
InputSource source = new InputSource(new StringReader(xml));
String dunsId = (String) expr.evaluate(source,XPathConstants.STRING);
return dunsId;
}
I believe that the problem lies with the XPathExpression:
XPathExpression expr = xpath.compile("/SOAP-ENV:Envelope/SOAP-ENV:Body/*/*/*/*/*/*/*/*/*/*/*[local-name()='PartyID']/text()");
I have tried a number of alternatives for 'expr' however none of these have worked. Has anyone got any ideas?
Because the xml you need to parse is sitting inside a CDATA block, you'll need to re-parse the value of s1:arg0 before accessing data within it.
You will need to do this in 2 steps
You will need to access the arg0 node in the http://rules.kmtool.abc.com namespace.
Since you don't have a NamespaceContext for this inner xmlns, you can use :
/SOAP-ENV:Envelope/SOAP-ENV:Body/*[local-name()='invokerules']
/*[local-name()='arg0']/text()
You then need to load this value into another InputSource.
The PartyId attribute can be accessed via the path:
kbdInitiateRequest/kmTestHeader/TestParty/ConductorParty/Party/#PartyID
(no need to use local-name() since there aren't any xmlns in the CDATA)
Notice that your inner xml is inside CDATA node.
So basiclly you are trying to query path of an XML inside CDATA.
As this thread state
Xpath to the tag inside CDATA
Seems this is not possible :(
I would suggest take the CData inside the code and parse it into a new XML Document and query that.
Thanks,
Amir

Why I can't take the content of a tag using XPath in a Java method?

I am very new in XPath and I have the following problem:
I have a Java method that receives data from a webservices and these data are in a XML document, so I have to use XPath to take a specific value inside this XML result document.
In particular I have that this is the entire XML output provided by my web service (the web service response):
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<getConfigSettingsResponse xmlns="http://tempuri.org/">
<getConfigSettingsResult><![CDATA[<root>
<status>
<id>0</id>
<message></message>
</status>
<drivers>
<drive id="tokenId 11">
<shared-secret>Shared 11</shared-secret>
<encoding>false</encoding>
<compression />
</drive>
<drive id="tokenId 2 ">
<shared-secret>Shared 2 </shared-secret>
<encoding>false</encoding>
<compression>false</compression>
</drive>
</drivers>
</root>]]></getConfigSettingsResult>
</getConfigSettingsResponse>
</s:Body>
</s:Envelope>
Now in a Java class I perform the following operations:
XPath xPath; // An utility class for performing XPath calls on JDOM nodes
Element objectElement; // An XML element
//xPath = XPath.newInstance("s:Envelope/s:Body/getVersionResponse/getVersionResult");
try {
// XPath selection:
xPath = XPath.newInstance("s:Envelope/s:Body");
xPath.addNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/");
objectElement = (Element) xPath.selectSingleNode(documentXML);
if (objectElement != null) {
result = objectElement.getValue();
System.out.println("RESULT:");
System.out.println(result);
}
} catch (JDOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
and the result of printing the content of the result variable is this output:
RESULT:
<root>
<status>
<id>0</id>
<message></message>
</status>
<drivers>
<drive id="tokenId 11">
<shared-secret>Shared 11</shared-secret>
<encoding>false</encoding>
<compression />
</drive>
<drive id="tokenId 2 ">
<shared-secret>Shared 2 </shared-secret>
<encoding>false</encoding>
<compression>false</compression>
</drive>
</drivers>
</root>
Now my problem is that I want to access only ad the content of the 0 tag, so I want that (in this case) my result variable have to contain the 0 value.
But I can't, I have try to change the previous XPath selection with:
xPath = XPath.newInstance("s:Envelope/s:Body/s:status/s:id");
But doing in this way I obtain that my objectElement is null
Why? What am I missing? What have I to do to obtain that mu result variable contains the content of the id tag?
Tnx
Andrea
Yours "root" node in "CDATA" section. Whole section interpetated as text, and you cannot search it by xPath. You can get text from "objectElement.getValue()", parse it like new XML, and then get tag "id" value with new xPath. Also you can search "objectElement.getValue()" for tag "id" value with regular expression.
Really you should be using the new XPathAPI in JDOM 2.x, and taking pasha701's answer in to consideration, your code should look more like:
Namespace soap = Namespace.getNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/");
Namespace tempuri = Namespace.getNamespace("turi", ""http://tempuri.org/");
XPathExpression<Element> xpath = XPathFactory.instance().compile(
"s:Envelope/s:Body/turi:getConfigSettingsResponse/turi:getConfigSettingsResult",
Filters.element(), null, soap, tempuri);
Element result = xpath.evaluateFirst(documentXML);
String resultxml = result.getValue();
Document resultdoc = new SAXBuilder().build(new StringReader(resultxml));
Element id = resultdoc.getRootElement().getChild("status").getChild("id");

Adding namespaces to the AXIOMXPath

XML :
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<wmHotelAvailResponse xmlns="http://host.com/subPath">
<OTA_HotelAvailRS Version="1.001">
</OTA_HotelAvailRS>
</wmHotelAvailResponse>
</soap:Body>
</soap:Envelope>
Code :
String xpathString = "/soap:Envelope/soap:Body/wmHotelAvailResponse/OTA_HotelAvailRS";
AXIOMXPath xpathExpression = new AXIOMXPath(xpathString);
xpathExpression.addNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
xpathExpression.addNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
xpathExpression.addNamespace("xsd", "http://www.w3.org/2001/XMLSchema");
OMElement rsMsg = (OMElement)xpathExpression.selectSingleNode(documentElement);
String version = rsMsg.getAttribute(new QName("Version")).getAttributeValue();
Question :
This is working perfectly when the xmlns="http://host.com/subPath" part is deleted. I wanna know how can I add xmlns="http://host.com/subPath" part to the xpathExpression to make the above work
I tried below but didn't work.
xpathExpression.addNamespace("", "http://host.com/subPath");
Solution:
.1. Add this code:
xpathExpression.addNamespace("x", "http://host.com/subPath");
.2. Change:
String xpathString = "/soap:Envelope/soap:Body/wmHotelAvailResponse/OTA_HotelAvailRS";
to:
String xpathString = "/soap:Envelope/soap:Body/x:wmHotelAvailResponse/x:OTA_HotelAvailRS";
Explanation:
Xpath always treats any unprefixed element name as belonging to "no namespace".
Therefore when evaluating the XPath expression:
/soap:Envelope/soap:Body/wmHotelAvailResponse/OTA_HotelAvailRS
the Evaluator tries to find a wmHotelAvailResponse element that is in "no namespace" and fails because the only wmHotelAvailResponse element in the document belongs to the "http://host.com/subPath" namespace.

Java - parse xml string with variable tagnames?

I'm trying to parse an XML string, and the tagnames are variable; I haven't seen any examples on how to pull the information out without knowing them. For example, I will always know the <response> and <data> tags below, but what falls inside/outside of them could be anything from <employee> to you name it.
<?xml version="1.0" encoding="UTF-8"?>
<response>
<generic>
....
</generic>
<data>
<employee>
<name>Seagull</name>
<id>3674</id>
<age>34</age>
</employee>
<employee>
<name>Robin</name>
<id>3675</id>
<age>25</age>
</employee>
</data>
</response>
You could parse it into a generic dom object and traverse it. For example, you could use dom4j to do this.
From the dom4j quick start guide:
public void treeWalk(Document document) {
treeWalk( document.getRootElement() );
}
public void treeWalk(Element element) {
for ( int i = 0, size = element.nodeCount(); i < size; i++ ) {
Node node = element.node(i);
if ( node instanceof Element ) {
treeWalk( (Element) node );
}
else {
// do something....
}
}
}
public Document parse(URL url) throws DocumentException {
SAXReader reader = new SAXReader();
Document document = reader.read(url);
return document;
}
I have seen similar situation in the projects.
If you are going to deal with large XMLs, you can use Stax or Sax parser to read the XML. On every step (like on reaching end element), enter the data into a Map or a dta structure of your choice, where you keep tag names as the key and value as value in the Map. Finally once you have the parsing done, use this Map to figure out which object to build as finally you would have a proper entity representation of the information in the XML
If XML is small,use DOM and directly build the entity object by reading the specific tag (like employee> or use XPATh to where you expect the tag to be present, giving you hint of the entity. Build that object directly by reading the specific information from the XML.

Categories

Resources