解决加载XSLT文件时的相对路径问题

20

我需要使用Apache FOP进行XSL转换,我有如下的代码:

//Setup FOP
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);
//Setup Transformer
Source xsltSrc = new StreamSource(new File(xslPath));
Transformer transformer = tFactory.newTransformer(xsltSrc);

//Make sure the XSL transformation's result is piped through to FOP
Result res = new SAXResult(fop.getDefaultHandler());
//Setup input
Source src = new StreamSource(new File(xmlPath));
//Start the transformation and rendering process
transformer.transform(src, res);

其中xslPath是我的XSLT文件存储的路径。

我确认当我只有一个XSLT文件时它是有效的,但在我的项目中,我将事物分为几个XSLT文件并使用<xsl:import />标签将它们连接起来。在这种配置下,我会得到一个空指针异常,因为它无法理解存储在不同文件中的所有XSLT信息。

我想知道是否有任何方法可以将所有这些文件加载到Source xsltSrc变量中,以便所有XSL信息都可用。

更新

根据Mads Hansen的答案,我已经更改了代码,但它仍然无法正常工作。我必须在类路径中包含XSLT slt文件,因此我使用ClassLoader加载XSLT文件。当执行url.toExternalForm()时,我已检查URL是否具有正确的路径。这是我的新代码片段:

ClassLoader cl = this.getClass().getClassLoader();
String systemID = "resources/xslt/myfile.xslt";
InputStream in = cl.getResourceAsStream(systemID);
URL url = cl.getResource(systemID);
Source source = new StreamSource(in);
source.setSystemId(url.toExternalForm());
transformer = tFactory.newTransformer(source);

它找到并加载了myfile.xslt,但仍然无法解析其他XSLT文件的相对路径。

我做错了什么?

4个回答

26

我刚刚明白了,一个晚回答(在FOP 1.0上测试通过)------

你只需要为你的工厂设置一个uri解析器,以下的设置对我有效:

TransformerFactory transFact = TransformerFactory.newInstance();
StreamSource xsltSource = new StreamSource(xsl);

// XXX for 'xsl:import' to load other xsls from class path
transFact.setURIResolver(new ClasspathResourceURIResolver());
Templates cachedXSLT = transFact.newTemplates(xsltSource);
Transformer transformer = cachedXSLT.newTransformer();


class ClasspathResourceURIResolver implements URIResolver {
  @Override
  public Source resolve(String href, String base) throws TransformerException {
    return new StreamSource(XXX.getClassLoader().getResourceAsStream(href));
  }
}
并且我要导入xsl文件(所以'imported.xsl'应该在类路径中):
<xsl:import href="META-INF/companybusinesscredit/imported.xsl"/>

非常感谢,这节省了我好几个小时的时间。 - Daniel Bo
我以不同的方式解决了这个问题,只需从工厂中调用setURIResolver方法。factory.setURIResolver(resolver); - Zhasulan Berdibekov
我以不同的方式解决这个问题,只需从工厂中调用setURIResolver方法。factory.setURIResolver(resolver); - undefined

14
当您将XSLT作为StreamSource加载时,如果没有设置SystemID,处理器不知道XSLT的"位置",无法解析相对路径。

http://www.onjava.com/pub/a/onjava/excerpt/java_xslt_ch5/index.html?page=5

通过将系统标识符作为参数提供给StreamSource,您告诉XSLT处理器在哪里查找commonFooter.xslt。如果没有这个参数,当处理器无法解析此URI时,可能会遇到错误。简单的解决方法是调用setSystemId()方法,如下所示:
// construct a Source that reads from an InputStream
Source mySrc = new StreamSource(anInputStream);
// specify a system ID (a String) so the 
// Source can resolve relative URLs
// that are encountered in XSLT stylesheets
mySrc.setSystemId(aSystemId);

你可能需要获取SystemID的绝对路径。除非你知道它将从哪里解析相对路径。尝试使用类似以下方式将相对路径转换为绝对路径:String xsltSystemId = new File(systemID).toURL().toExternalForm( );或者String xsltSystemId = new File(relativePath).getAbsolutePath(); - Mads Hansen
谢谢,我已经考虑过这个问题并检查过了。当我执行url.toExternalForm()时,它会得到一个绝对路径。但是我现在不知道问题出在哪里。 - Javi
5
这个解决方案完美适用于我:xsltStreamSource.setSystemId(xsltFile.toURI().toString()); - james.garriss
1
@james.garriss 在爬行网络和阅读无用评论几个小时后,终于有了你这个“一句话”解决了我相对'import'问题的难题。如果可以的话,我会给你10000个点赞!:D - M'λ'
很高兴能帮助你,@M'λ'。 - james.garriss
显示剩余3条评论

3

我正在使用Saxon 9.x,但在样式表中使用document时仍然遇到了问题。虽然使用了setSystemId,但与样式表捆绑在一起的xml文件在jar文件中未能按预期加载,导致文件未找到异常。对我来说,使用下面的代码自定义解析器更容易:

JarfileResolver jarfileResolver = new JarfileResolver();
transformer.setURIResolver(jarfileResolver);


public class JarfileResolver implements URIResolver
{
    public Source resolve(String fileName, String base) throws TransformerException
    {
        URL url = getClass().getClassLoader().getResource(fileName);
        StreamSource jarFileSS = new StreamSource();

        try
        {
            InputStream jarfileIS = url.openStream();
            jarFileSS.setInputStream(jarfileIS);
        }
        catch(IOException ioExp)
        {
            throw new TransformerException(ioExp);
        }
        return jarFileSS;
    }
}

0

使用 xmlschema-core-2.2.4.jar 后,接口 URIResolver 发生了变化,现在有三个参数:targetNamespaceschemaLocationbaseUri

根据 @JunLei 的回答,现在应该是这样的:

TransformerFactory transFact = TransformerFactory.newInstance();
URIResolver classpathResourceURIResolver= (targetNamespace, schemaLocation, baseUri) -> new InputSource(MyClass.class.getClassLoader().getResourceAsStream(schemaLocation));
transFact.setURIResolver(classpathResourceURIResolver);
...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接