Apache Camel Xpath 2.0 with Saxon 看起来不能在 RouteBuilder / Predicates 中工作

Apache Camel Xpath 2.0 with Saxon does not look to work in RouteBuilder / Predicates

我正在使用包含 Camel 2.10.7 的 ServiceMix 4.5.3,我能够使用像这样的选项端点对 Saxon 库进行 XSLT 2.0 转换:

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

然而,当我尝试像这样使用 xpath 函数时:

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"));

如果我执行上面的代码,它说 tokenize() 函数未知。我猜是因为它仍然使用 xalan (xpath 1.0) 而不是 Saxon。

我也尝试像在 Camel 文档中那样附加 .saxon()

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

但是报错找不到Saxon实现工厂:

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

在我的 OSGI 上下文中,我验证了 camel-saxonApache ServiceMix :: Bundles :: saxon9he (9.3.0.11_2) 都已部署。

我们计划尽快升级到 ServiceMix 5,但我不知道这个问题是否仍然存在,4.5.3 版本的解决方案对我来说会更好。

在 Saxon 9.6 中,我们删除了将 Saxon 注册为 JAXP XPathFactory 接口实现的服务文件。它仍然实现该接口,只是在 JAR 文件清单中没有这样说明的服务文件。这背后有很长的历史,但基本上有两个主要原因:(a) JDK 版本之间的不兼容性导致无法生成适用于 JDK 5 到 JDK 8 的服务文件包容性,并且 (b) 仅仅将 Saxon 放在类路径中会导致应用程序中断,如果它们没有被编写或测试为与 XPath 2.0 一起工作。

我想有一个解决方法,就是自己将服务文件添加到 SAXON Jar 文件清单中(您可以从 9.5 版本复制它)。

我终于找到了骆驼路线的可行解决方案:

由于 .saxon() 对我的路线没有影响,我决定换一种方式,我发现有一个特定的选项可以像这样覆盖 Xpath 工厂:

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

之后,我不得不使用 .resultType(String.class) 将 Xpath 的结果强制转换为字符串。

完整的谓词如下所示:

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

现在标记化函数 (xpath 2.0) 得到了很好的认可。

我发现当我在骆驼处理器中使用它时,我必须对工厂实现的实例化做同样的事情:

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);

这不是动态的,但我想在任何地方强制使用 Saxon,这样这个解决方案就符合我们的需要。