What I did not mention there is how to do xpath-queries on documents with namespaces.
Take for example the following xml:
<?xml version="1.0" encoding="UTF-8" ?>
<XSLBatch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.darwin-it.nl/XMLTypes/XSLBatch ../xsd/XSLBatch.xsd"
xmlns="http://www.darwin-it.nl/XMLTypes/XSLBatch">
<XSLTransform>
<Order>1</Order>
<SourceXMLFile>/home/makker/Projects/Java/trunk/src/JavaAndXML/XMLTester/workspace/xml/sos_exportjobs.xml</SourceXMLFile>
<XSLFile>/home/makker/Projects/Java/trunk/src/JavaAndXML/XMLTester/workspace/xsl/CreateObjectType_0.2.xsl</XSLFile>
<DestinationFile>/home/makker/Projects/Java/trunk/src/JavaAndXML/XMLTester/workspace/output/sos_exportjobs.tps</DestinationFile>
</XSLTransform>
<XSLTransform>
<Order>2</Order>
<SourceXMLFile>/home/makker/Projects/Java/trunk/src/JavaAndXML/XMLTester/workspace/xml/sos_exportjobs.xml</SourceXMLFile>
<XSLFile>/home/makker/Projects/Java/trunk/src/JavaAndXML/XMLTester/workspace/xsl/CreateObjectTypeBody_0.2.xsl</XSLFile>
<DestinationFile>/home/makker/Projects/Java/trunk/src/JavaAndXML/XMLTester/workspace/output/sos_exportjobs.tpb</DestinationFile>
</XSLTransform>
</XSLBatch>
If you want to do a query on all XSLTransforms, you would probably write an Xpath of the form:
/XSLBatch/XSLTransform
But if you try to do that with the statement:
private XMLDocument xmlDoc;
...
public NodeList selectNodes(String xpath) throws XSLException {
NodeList nl = this.xmlDoc.selectNodes(xpath);
return nl;
}
then you find out that the NodeList nl would not deliver you any nodes.
This is because the XML document is of namespace: "http://www.darwin-it.nl/XMLTypes/XSLBatch" and this is not taken into account in the xpath expression above.
You could use an expression in the form of:
/xbt:XSLBatch/xbt:XSLTransform
But then: how does the parser know to what Namespace the abbreviation xbt resolves?
You could avoid the problem above with the expression:
/*[local-name()="XSLBatch"]/*[local-name()="XSLTransform"]
This is especially usefull if you have no means of specifying the namespaces you used.
But it makes the expressions very complex and if you need to query on a specific value of an attribute then you're out.
You can resolve this in Java quite easily with a NameSpace resolver. A what? A NameSpace resolver is a class that implements the oracle.xml.parser.v2.NSResolver interface. That is: using the Oracle XML parser.
A sample implementation of the Namespace Resolver is as follows:
package com.m10.xmlfiles; import java.util.HashMap; import oracle.xml.parser.v2.NSResolver; public class XMLNSResolver implements NSResolver{ private HashMap nsMap = new HashMap(); public XMLNSResolver() { } public void addNS(String abbrev, String namespace){ nsMap.put(abbrev, namespace); } public String resolveNamespacePrefix(String string) { return (String)nsMap.get(string); } }
As you can see this implementation is fairly simple. It has a HashMap in which you can store your namespaces with an abbreviation as a key.
Since it inmplements the NSResolver interface it must implement the resolveNamespacePrefix. And that one does exactly what you might expect: it gives the namespace back that belongs to the abbreviation.
So if you do the following:
XMLNSResolver nsRes = new XMLNSResolver();
nsRes.addNS("xbt", "http://www.darwin-it.nl/XMLTypes/XSLBatch");
Then you can do your xpath query as follows:
NodeList nl = this.xmlDoc.selectNodes("/xbt:XSLBatch/xbt:XSLTransform", nsRes);
And that is how this is done.
Good blog.
ReplyDeletePortugal
Thanks alot for your solution.
ReplyDelete