<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ds="http://www.ilog.com/rules/DecisionService" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<DecisionServiceResponse >
<DecisionID>RLOS001PL62000205DRAFT_2019-07-05-13:35:57:48957</DecisionID>
<underwritingRequest xmlns="http://www.ilog.com/rules/param">
<ns0:underwritingApprovalRequest xmlns:ns0="http://www.tmbbank.com/enterprise/model">
<application xmlns="">
<projectCode/>
I have a Java class that implements javax.xml.namespace.NamespaceContext which later will be instantiated and set in the setNamespaceContext of the class javax.xml.xpath.XPath. Below showing how I override the 3 methods from the NamespaceContext interface.
/**
* This method is called by XPath. It returns the default namespace, if the
* prefix is null or "".
*
* @param prefix to search for
* @return uri
*/
public String getNamespaceURI(String prefix) {
if (prefix == null || prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) {
return prefix2Uri.get(DEFAULT_NS);
} else {
return prefix2Uri.get(prefix);
}
}
//This method is not needed in this context, but can be implemented in a similar way.
public String getPrefix(String namespaceURI) {
return uri2Prefix.get(namespaceURI);
}
public Iterator<String> getPrefixes(String namespaceURI) {
// Not implemented
return null;
}
Apart from the above, there is another method as below:
private void putInCache(String prefix, String uri) {
prefix2Uri.put(prefix, uri);
uri2Prefix.put(uri, prefix);
}
private void storeAttribute(Attr attribute) {
// examine the attributes in namespace xmlns
if (attribute.getNamespaceURI() != null
&& attribute.getNamespaceURI().equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) {
// Default namespace xmlns="uri goes here"
if (attribute.getNodeName().equals(XMLConstants.XMLNS_ATTRIBUTE)) {
putInCache(DEFAULT_NS, attribute.getNodeValue());
} else {
// Here are the defined prefixes stored
putInCache(attribute.getLocalName(), attribute.getNodeValue());
}
}
}
The above method would not work because are multiple xmlns declarations, and prefix2Uri is a HashMap which does not allow duplicates, hence the prefix DEFAULT will be set to the most latest xmlns="" declaration than the one highlighted in yellow as above.
Let me print something below for your clarity:
ns0, http://www.tmbbank.com/enterprise/model
DEFAULT, http://www.ilog.com/rules/param
DEFAULT, ""
As you can see, eventually the prefix2Uri HashMap contains only 2 entries:
ns0, http://www.tmbbank.com/enterprise/model
DEFAULT, ""
In order to fix this, just need to add one line in the putInCache method that looks like below, highlighted in light blue:
private void putInCache(String prefix, String uri) {
if (prefix2Uri.get(prefix) == null || prefix2Uri.get(prefix).equals("")) {
prefix2Uri.put(prefix, uri);
uri2Prefix.put(uri, prefix);
}
}
Besides that, the XPath query that is used to query the XML, also needs to add DEFAULT as the prefix.
Initially, the method that builds the XPath is as below:
public static String getPath(Node n) {
StringBuilder path = new StringBuilder();
do {
path.insert(0, n.getNodeName());
path.insert(0, "/");
} while ((n = n.getParentNode()) != null && n.getNodeType() == Node.ELEMENT_NODE);
return path.toString();
}
Now, it should look like the below , added lines highlighted in purple:
public static String getPath(Node n) {
StringBuilder path = new StringBuilder();
do {
if(n.getNamespaceURI() != null && !n.getNamespaceURI().equals("") && (n.getPrefix() == null || n.getPrefix().equals(""))){ path.insert(0, UniversalNamespaceCache.DEFAULT_NS + ":" + n.getNodeName());
} else {
path.insert(0, n.getNodeName());
}
path.insert(0, "/");
} while ((n = n.getParentNode()) != null && n.getNodeType() == Node.ELEMENT_NODE);
return path.toString();
}