带有Saxon的Apache Camel Xpath 2.0看起来不适用于RouteBuilder / Predicates

时间:2015-02-04 13:23:37

标签: xpath apache-camel saxon xpath-2.0

我正在使用包含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版本的解决方案对我来说会更好。

2 个答案:

答案 0 :(得分:3)

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

我想通过自己将服务文件添加到SAXON Jar文件清单中来解决这个问题(您可以从9.5版本中复制它)。

答案 1 :(得分:1)

最后,我找到了骆驼路线的有效解决方案:

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

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

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

完整谓词将如下所示:

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

现在,tokenize函数(xpath 2.0)得到了很好的认可。

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

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,然后这个解决方案符合我们的需求。