8

I use Xalan in my application, but need to use Saxon with a reference implementation to generate test output to compare with. I want to use them both during unit tests. However, as soon as I add an dependency on Saxon in the project .pom, the application seems to use Saxon for all xslt and XPath operations during tests:

<dependency>
  <groupId>net.sf.saxon</groupId>
  <artifactId>Saxon-HE</artifactId>
  <version>9.4</version>
  <scope>test</scope>
</dependency>

This makes the main application fail when generating output due to a different XPath behaviour. When running the main application outside of test scope it works.

How can I run the main application using Xalan, but the tests using Saxon, during tests?

I have tried setting the following property before running the Xalan and Saxon parts:

System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.processor.TransformerFactoryImpl ");
System.setProperty("javax.xml.transform.TransformerFactory", "net.sf.saxon.TransformerFactoryImpl");

I have also tried to put the Xalan and Saxon parts in different projects, and I have also tried to use them both from a third project, with the same result.

Danik
  • 404
  • 3
  • 14

2 Answers2

11

Avoid relying on the JAXP factory mechanism for selecting your transformation engine. Instead load the engine you want explicitly: it's much more reliable and much faster. For Saxon, replace the call on

TransformerFactory.newInstance()

with

new net.sf.saxon.TransformerFactoryImpl()

and for Xalan use

new org.apache.xalan.processor.TransformerFactoryImpl()
Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • It it the XPath part that is failing though. Is there a way to tell XPathFactory to use the default implementation? This answer says how to do the opposite, use the Saxon implementation http://stackoverflow.com/questions/926222/using-saxon-xpath-engine-in-java. – Danik Jun 27 '12 at 15:19
  • 1
    That would be org.apache.xpath.jaxp.XPathFactoryImpl as per http://www.jarvana.com/jarvana/view/xalan/xalan/2.7.0/xalan-2.7.0.jar!/org/apache/xpath/jaxp/XPathFactoryImpl.class?classDetails=ok. Anyway, I'd suggest to use the JAXP mechanism to for your Application instead of making it implementation dependent unless you have extreme use cases in your application. Only use impl dependent code in tests. – Kasun Gajasinghe Jun 27 '12 at 15:31
  • 2
    I disagree. A lot of people trip up because they use the JAXP mechanism and pick up an XPath 2.0 processor when their application requires an XPath 1.0 processor. You can't explicitly ask for an XPath 1.0 processor; if you don't say what you want, you don't know what you'll get, and it may not work. – Michael Kay Jun 27 '12 at 21:55
  • I had to use Saxon because the reference implementation relied on it. I got it to work based on your answers, thanks! (see the solution in my answer below) – Danik Jun 28 '12 at 08:57
  • An update to the answer: it's still valid for XSLT, but for XPath, Saxon no longer declares itself as a JAXP XPath factory provider. Thats because the JAXP API for XPath is so loosely defined (a lot of parameters declared as "Object" where it's implementation-defined what is actually accepted) that an application written for one XPath processor will rarely work with a different one, especially when it implements a different version of the XPath language. – Michael Kay Jan 10 '18 at 08:17
2

Here is the solution for completeness:

System.setProperty(XPathFactory.DEFAULT_PROPERTY_NAME + ":"
    + XPathFactory.DEFAULT_OBJECT_MODEL_URI,
    "org.apache.xpath.jaxp.XPathFactoryImpl");
System.setProperty(XPathFactory.DEFAULT_PROPERTY_NAME + ":"
    + NamespaceConstant.OBJECT_MODEL_SAXON,
    "net.sf.saxon.xpath.XPathFactoryImpl");

XPathFactory jaxpFactory =
    XPathFactory.newInstance(XPathFactory.DEFAULT_OBJECT_MODEL_URI);
XPathFactory saxonFactory =
    XPathFactory.newInstance(NamespaceConstant.OBJECT_MODEL_SAXON);
Gray
  • 115,027
  • 24
  • 293
  • 354
Danik
  • 404
  • 3
  • 14