Dans les versions antérieures à la 12c, il était possible d’intégrer une fonction d’évaluation dynamique de xpath, au travers de la méthode « evaluate » du jar de xalan par exemple.
Suite au passage sur le moteur « Oracle XDK XSLT » il est désormais plus compliqué d’implémenter cette fonctionnalité.
Voici donc un exemple d’implémentation de « custom xpath » fonction permettant de réaliser du « xpath dynamique ».
1 – Création d’un jar implémentant une méthode « evaluate ».
Cette méthode possède 2 paramètres en entrée, une expression Xpath d’une part et un objet XML sur lequel appliquer le Xpath. La classe « UniversalNamespaceResolver » permet de résoudre les namespaces déclarés dans la balise racine de l’objet XML.
public static String evaluate(final String expression, org.apache.xmlbeans.XmlObject xmlObject) throws XPathFunctionException { String str=""; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); try { //parse initial xmlObject InputStream inputStream = xmlObject.newInputStream(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(inputStream); document.getDocumentElement().normalize(); //initialize xpath object and get namespace context from root node. XPathFactory xpf = XPathFactory.newInstance(); XPath path = xpf.newXPath(); path.setNamespaceContext(new UniversalNamespaceResolver(document)); //create a new document removing xml-fragment element DocumentBuilder builderWithoutFragment = factory.newDocumentBuilder(); Document documentWithoutFragment = builderWithoutFragment.newDocument(); Node firstChildNode = document.getDocumentElement().getChildNodes().item(0); Node importedNode = documentWithoutFragment.importNode(firstChildNode,true); documentWithoutFragment.appendChild(importedNode); str = path.evaluate(expression, documentWithoutFragment); return str; } catch (Exception exc) { throw new XPathFunctionException(exc.toString() + " - Failed to evaluate xpath expression in custom xpath function."); } }
public class UniversalNamespaceResolver implements NamespaceContext { // the delegate private Document sourceDocument; /** * This constructor stores the source document to search the namespaces in * it. * * @param document * source document */ public UniversalNamespaceResolver(Document document) { sourceDocument = document; } /** * The lookup for the namespace uris is delegated to the stored document. * * @param prefix * to search for * @return uri */ public String getNamespaceURI(String prefix) { if (prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) { return sourceDocument.lookupNamespaceURI(null); } else { return sourceDocument.lookupNamespaceURI(prefix); } } /** * This method is not needed in this context, but can be implemented in a * similar way. */ public String getPrefix(String namespaceURI) { return sourceDocument.lookupPrefix(namespaceURI); } public Iterator getPrefixes(String namespaceURI) { // not implemented yet return null; } }
2 – Modification des fichiers « osb-built-in.properties » et « osb-built-in.xml » situés dans $ORACLE_HOME\osb\config\xpath-functions.
Ces fichiers permettent de déclarer la nouvelle fonction « evaluate » afin qu’elle soit reconnue dans l’osb.
%MY_FUNCTIONS%=My custom Bus Functions %FUNC_EVALUATE_COMMENT%=evaluate xpath over a document E.g. evaluate(expression as xs:string,xml as xs:tree)
<xpf:category id="%MY_FUNCTIONS%"> <xpf:function> <xpf:name>evaluate</xpf:name> <xpf:comment>%FUNC_EVALUATE_COMMENT%</xpf:comment> <xpf:namespaceURI>http://www.my-custom-xpath.com/xpath</xpf:namespaceURI> <xpf:className>com.monpackage.EvaluateXpath</xpf:className> <xpf:method>java.lang.String evaluate(java.lang.String, org.apache.xmlbeans.XmlObject)</xpf:method> <xpf:isDeterministic>false</xpf:isDeterministic> <xpf:scope>Pipeline</xpf:scope> <xpf:scope>SplitJoin</xpf:scope> </xpf:function> </xpf:category>
remarque : un redémarrage du noeud d’administration et OSB est nécessaire pour une prise en compte du jar et de la déclaration de la nouvelle fonction xpath.
3 – Exemple de xquery utilisant la fonction « evaluate ».
La fonction « addContext » permet de déclarer une liste de namespace qui seront ensuite « résolus » par la classe « UniversalNamespaceResolver » afin d’être utilisés dans les expressions xpath.
La fonction « evaluate » utilise la fonction custom « evaluate ».
declare function local:addContext($document as element()) as element() { <context xmlns:con="http://www.bea.com/wli/sb/context" xmlns:tran="http://www.bea.com/wli/sb/transports" xmlns:http="http://www.bea.com/wli/sb/transports/http" xmlns:conf="http://www.bea.com/wli/sb/stages/transform/config" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 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" xmlns:jca="http://www.bea.com/wli/sb/transports/jca">{$document}</context> }; declare function local:evaluate($expression as xs:string, $document as element()) as element() { <evaluateResult>{xpatVN:evaluate($expression,local:addContext($document))}</evaluateResult> };