我正处于概念验证阶段,要将一些DocbookPDF转换为→应用程序.基本要求是:

  • 它必须"从JAR中运行"--在应用程序服务器的文件系统上设置样式表as files不是我想要的.
  • 它是基于Spring的not,所以我想要一种更通用的Java解决方案.
  • 我们目前使用的是DocBook 1.79.2样式表,但如果更合适的话,可能会使用xslt20 stylesheets样式表.
  • 我们目前正在概念验证中使用Saxon-HE 12.3,但绝对可以将其升级到商业版本.

TLDR是:如何封装DocBook XSLT样式表in a JAR(doesn't需要将JAR分解成文件系统上的文件)?

作为docbook-app邮件列表中的recently discussed,我可以从src/main/resources/xsl(在该级别进行一些定制,然后是src/main/resources/xsl/docbook-xsl-1.79.2中的DocBook样式表)中的样式表开始,这是一个如下所示的目录:

<?xml version="1.0" encoding="utf-8"?>
<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
   <uri name="file:/xsl/juno-driver.xsl"
         uri="classpath:/xsl/juno-driver.xsl" />
   <uri name="file:/xsl/header-footer.xsl"
         uri="classpath:/xsl/header-footer.xsl" />
   <uri name="file:/xsl/table.xsl"
         uri="classpath:/xsl/table.xsl" />
   <uri name="file:/xsl/titlepage.xsl"
         uri="classpath:/xsl/titlepage.xsl" />
   <uri name="file:/xsl/docbook-xsl-1.79.2/fo/docbook.xsl"
         uri="classpath:/xsl/docbook-xsl-1.79.2/fo/docbook.xsl" />
   <uri name="file:/xsl/docbook-xsl-1.79.2/VERSION.xsl"
         uri="classpath:/xsl/docbook-xsl-1.79.2/VERSION.xsl" />
   <uri name="file:/xsl/docbook-xsl-1.79.2/fo/param.xsl"
         uri="classpath:/xsl/docbook-xsl-1.79.2/fo/param.xsl" />

(并继续将每个.xsl.xml.ent.dtd文件映射到其classpath: URI等效项),以及一些如下代码:

DOMResult result = new DOMResult();
TransformerFactory factory = TransformerFactory.newInstance();
InputStream is = XmlTest.class.getResourceAsStream("/xsl/juno-driver.xsl");
Source source = new StreamSource(is, "file:/xsl/juno-driver.xsl");
Transformer transformer = factory.newTransformer(source);
transformer.transform(new DOMSource(document), result);
return (Document) result.getNode();

almost个能让我们达到目标,但失败了:

Error at char 9 in expression in xsl:param/@select on line 18 column 57 of l10n.xsl:
  FODC0002  I/O error reported by XML parser processing
  file:///xsl/docbook-xsl-1.79.2/common/l10n.xsl. Caused by java.io.FileNotFoundException:
  /xsl/docbook-xsl-1.79.2/common/l10n.xsl (No such file or directory)
at parameter local.l10n.xml on line 18 column 57 of l10n.xsl:
     invoked by global parameter local.l10n.xml at file:///xsl/docbook-xsl-1.79.2/common/l10n.xsl#18

如果该线路涉及对document('')的呼叫:

<xsl:param name="local.l10n.xml" select="document('')"/>

看起来它坚持从一个文件加载自己,然后(显然)在那个URI找不到它.我们如何告诉正在解析对document()函数的调用的人使用类路径呢?

我已经向gihub提出了minimal example个问题:你可以克隆回购,然后运行mvn clean test来复制.

我也愿意接受any other approach条建议来完成这项工作,以满足帖子顶部的限制列表!

推荐答案

我认为有多种方法可以做到这一点.实现这一点的一种方法是添加对通过URL访问类路径中资源的支持.这样,您就可以使用URL指向类路径中的样式表,而不必准备好目录.

例如,您可以通过将下面的类注册为URLStreamHandlerProvider实现来实现它.该实现改编自this answer,但更改为支持URL路径中可选的前导斜杠,并且还更改为使用cp:方案名称,而不是更常规的classpath:.

  • 前导斜杠在URL中很有用,这样它们就可以被视为分层URL,从而可以解析相对引用.
  • 将方案(协议)名称更改为cp:是因为Saxon-HE(至少12.3版)似乎有一个针对classpath:个URL的解决方法,这会导致在解析相对classpath:个URL时删除路径中的前斜杠.

在Java 9及更高版本中,您可以通过将类的完全限定名放入配置文件META-INF/services/java.net.spi.URLStreamHandlerProvider来注册提供程序.

这样做之后,只要您的XSLT处理程序使用(或至少是退回到)这种取消引用URL的方法,您就应该能够指向具有类似cp:/xsl/docbook-xsl-1.79.2/html/docbook.xsl的URL的样式表,并使其在没有目录的情况下工作,包括相对导入.根据快速测试,这种方法似乎至少适用于Xalan-Java和Saxon-HE XSLT处理程序.(我认为Java附带的默认XSLT处理器在使用docbook-xsl样式表时可能会有一些问题.)

package com.stackoverflow.q76848364;

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.net.spi.URLStreamHandlerProvider;

/**
 * URL stream handler for "cp:/" URLs for accessing resources in the classpath.
 * Supports a leading slash in the the path so that the scheme is treated as a
 * hierarchical scheme for resolving relative URL references.
 * 
 * <p>
 * Register this provider by putting the fully qualified name of this class in
 * the configuration file
 * META-INF/services/java.net.spi.URLStreamHandlerProvider.
 */
public class ClasspathURLStreamHandlerProvider extends URLStreamHandlerProvider {

    private static final String PROTOCOL = "cp";

    @Override
    public URLStreamHandler createURLStreamHandler(String protocol) {
        if (PROTOCOL.equals(protocol)) {
            return new URLStreamHandler() {
                @Override
                protected URLConnection openConnection(URL url) throws IOException {
                    String urlPath = url.getPath();
                    String resourcePath = urlPath.startsWith("/") ? urlPath.substring(1) : urlPath;
                    return ClassLoader.getSystemClassLoader().getResource(resourcePath).openConnection();
                }
            };
        }
        return null;
    }

}

Edited to add: Caution about resolving relative URI references in Java

在Java中处理相对URI引用时,请注意,当相对URI为空时,there is a bug in the java.net.URI.resolve() method会影响解析相对URI引用(Java错误数据库中的错误JDK-8218962).Docbook-xsl样式表依赖于这一点才能正常工作,所以如果试图使用依赖于java.net.URI类的任何东西来实现这一功能,就会出现问题.既然Xalan-Java和Saxon-HE似乎都工作得很好,他们一定是在使用其他东西.

Edited to add (2): Demonstration

针对所提供的最小示例,我创建了一个pull request来演示此解决方案.(最初的示例设置为以Java 8为目标.由于注册URLStreamHandler个实现的方法在Java 8和Java 9+之间不同,我将编译目标改为Java 9,以演示较新的方法.)

Java相关问答推荐

无法运行Java(已解决)

gitlab ci不会运行我的脚本,因为它需要数据库连接'

如何在Docker容器中使用wireock—Webhooks阻止请求?

Character::Emoji不支持带数字的字符吗?

Spring Boot Maven包

Java中的死锁及其重入锁和锁

更新GWT 2.5.1到2.11.0和sencha GXT 3.1.1到4.1时出现错误

Spring和可编辑";where";@Query

OpenGL ES 3.0-纹理黑色

try 从REST API返回对象列表时出错

Instancio未在日志(log)中显示测试失败消息

Java.time.OffsetDateTime的SQL Server数据库列类型是什么?

用于Java的Visual Studio代码完成不起作用

为什么没有加载java.se模块?

如何在Struts2中使用操作类中的结果注释重定向到不同的命名空间

Bash数组的单引号元素并使用空格连接

将@Transactional添加到Spring框架中链下的每个方法会产生什么效果?

这是JavaFX SceneBuilder的错误吗?

双对象供应商

如何在Java中正确实现填字游戏求解器