Apache Camel Xpath 2.0 with Saxon does not look to

2020-07-25 03:21发布

I am using ServiceMix 4.5.3 which includes Camel 2.10.7 and I am able to make XSLT 2.0 transformations with the Saxon library using the option endpoint like this:

...
to("xslt:stylesheet.xsl?transformerFactoryClass=net.sf.saxon.TransformerFactoryImpl")
...

However when I try to use the xpath function like this:

private Namespaces ourNS = new Namespaces("myns",
        "urn:com:company:domain:namespace:myns/1");

// ... some code ...

// Make a predicate to filter according a header : 
// The code attribute looks like: urn:phone:apple:iphone:4s
Predicate isNotSamePhoneBrand = PredicateBuilder.isNotEqualTo(
        xpath("tokenize(/myns:Phone/@code, ':')[3]").namespaces(ourNS),
        header("PhoneBrand"));

If I execute the above code it says the tokenize() function is unknown. I guess it is because it still uses xalan (xpath 1.0) instead Saxon.

I also tried as well to append the .saxon() as in the Camel documentation

Predicate isNotSamePhoneBrand = PredicateBuilder.isNotEqualTo(
        xpath("tokenize(/myns:Phone/@code, ':')[3]").namespaces(ourNS).saxon(),
        header("PhoneBrand"));

But it makes an error that he cannot find the Saxon implementation factory:

Caused by: javax.xml.xpath.XPathFactoryConfigurationException: No XPathFctory implementation found for the object model: http://saxon.sf.net/jaxp/xpath/om

In my OSGI context I verified that both camel-saxon and Apache ServiceMix :: Bundles :: saxon9he (9.3.0.11_2) are deployed.

We are planning to upgrade to ServiceMix 5 soon but I do not know if this problem is still there or not, a solution for the version 4.5.3 would be better for me.

2条回答
劳资没心,怎么记你
2楼-- · 2020-07-25 03:41

Finally I found a working solution for the camel route:

As the .saxon() was without effect on my route, I decided to look another way and I found there is a specific option to override the Xpath factory like this:

.factory(new net.sf.saxon.xpath.XPathFactoryImpl())

After that I had to force the conversion of the result of the Xpath into a String with .resultType(String.class).

The full predicate will looks like this:

Predicate isNotSamePhoneBrand = PredicateBuilder.isNotEqualTo(
        xpath("tokenize(/myns:Phone/@code, ':')[3]").namespaces(ourNS)
        .factory(new net.sf.saxon.xpath.XPathFactoryImpl()).resultType(String.class),
        header("PhoneBrand"));

and now the tokenize function (xpath 2.0) is very well recognized.

I found that I have to do the same thing with the instantiation of the factory implementation when I use it in a camel Processor like this:

net.sf.saxon.xpath.XPathFactoryImpl factory = new net.sf.saxon.xpath.XPathFactoryImpl();
XPath xpath = factory.newXPath();

NodeList channels = (NodeList) xpath.evaluate(
    "//*:Channels/*:Channel/text()",
    body, XPathConstants.NODESET);

This is not dynamic but I want to force the usage of Saxon everywhere then this solution fit our needs.

查看更多
再贱就再见
3楼-- · 2020-07-25 03:44

In Saxon 9.6 we removed the services file that registered Saxon as an implementation of the JAXP XPathFactory interface. It still implements that interface, it just doesn't have a services file in the JAR file manifest that says so. There's a long history behind this, but there are basically two main reasons: (a) incompatibility between JDK releases making it impossible to produce a services file that works from JDK 5 to JDK 8 inclusive, and (b) merely putting Saxon on the classpath was causing applications to break if they were not written or tested to work with XPath 2.0.

I guess there's a workaround by adding the services file to the SAXON Jar file manifest yourself (you can copy it from the 9.5 release).

查看更多
登录 后发表回答