繁体   English   中英

Java 使用 Saxon (s9api) 转换 XML:如何在资源中添加输入文件?

[英]Java using Saxon (s9api) to transform XML: How to add input files in resources?

我使用 saxon (HE 9.9.1-6) 将 XML 转换为 HTML 文件。 使用 Saxon 是因为 XSLT 是版本 2 并且默认的 java 类失败。
XSLT 包含两个要复制到其他文件内容中的语句:

<xsl:value-of select="unparsed-text('file.ext')"/>

只要 Xslt 和这些文件在同一目录中,并且 xslt 作为文件源给出,这就可以正常工作

Source xslt = new StreamSource(new File("c:/somedir/file.xsl"));

但是我的 xslt 位于资源目​​录中(稍后它应该被打包在一个 jar 中)。 如果我在该上下文中使用它,则 saxon 无法找到包含的文件,因为它在我的项目的根目录中查找:

Source xslt = new StreamSource(getClass().getClassLoader().getResourceAsStream("file.xsl"));

结果是:

Error evaluating (fn:unparsed-text(...)) in xsl:value-of/@select on line 22 column 66
FOUT1170: Failed to read input file: <project root directory>\included_file.css (File not found)

有什么方法可以为撒克逊人提供额外的 StreamSources 来处理它需要包含的文件吗? 我无法找到任何东西。

理想情况下,我想要这样的东西:

transformer.addInput(new StreamSource(getClass().getClassLoader().getResourceAsStream("inputfile.css")));

我想出的唯一解决方案非常难看:将 xslt 和它需要的文件从资源复制到临时目录,然后使用它作为源进行转换。


示例代码

我不擅长编写 XSLT,所以我只能提供非最小示例文件。
可以在此处找到 xslt 及其两个需要的文件(css 和 js)。 您需要的是三个“xrechnung”。 直接链接: xrechnung-html.xslxrechnung-viewer.cssxrechnung-viewer.js
请将它们放在资源目录中(以防万一,在 eclipse 中:创建一个资源文件夹并将其添加为属性-> 构建路径中的源目录)。

xml 是由上述项目的第一步使用它自己的示例文件生成的,我把它放在了 pastebin这里
(最初直接包含但出现字符限制错误)

最后,Java 代码包括丑陋的解决方法:

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Comparator;

import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;

import org.xml.sax.SAXException;

import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.Serializer;
import net.sf.saxon.s9api.Xslt30Transformer;
import net.sf.saxon.s9api.XsltCompiler;
import net.sf.saxon.s9api.XsltExecutable;

public class SaxonProblem {
    public static void main(String[] args) throws IOException, SaxonApiException, SAXException {
        Path xml = Paths.get("path/to/the.xml");
        //working(xml);
        notWorking(xml);
    }

    public static void working(Path xmlFile) throws IOException, SaxonApiException, SAXException {
        Path dir = Files.createTempDirectory("saxon");
        System.out.println("Temp dir: " + dir.toString());
        Path xsltFile = dir.resolve("xrechnung-html.xsl");

        Files.copy(SaxonProblem.class.getClassLoader().getResourceAsStream("xrechnung-html.xsl"),
                xsltFile, StandardCopyOption.REPLACE_EXISTING);
        Files.copy(SaxonProblem.class.getClassLoader().getResourceAsStream("xrechnung-viewer.css"),
                dir.resolve("xrechnung-viewer.css"), StandardCopyOption.REPLACE_EXISTING);
        Files.copy(SaxonProblem.class.getClassLoader().getResourceAsStream("xrechnung-viewer.js"),
                dir.resolve("xrechnung-viewer.js"), StandardCopyOption.REPLACE_EXISTING);

        // for the sake of brevity, the html is made where the xml was
        Path html = xmlFile.resolveSibling(xmlFile.getFileName().toString() + ".html");
        Source xslt = new StreamSource(xsltFile.toFile());
        Source xml = new StreamSource(xmlFile.toFile());

        transformXml(xml, xslt, html);

        // cleanup
        Files.walk(dir).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
    }

    public static void notWorking(Path xmlFile) throws SaxonApiException, SAXException, IOException {
        // for the sake of brevity, the html is made where the xml was
        Path html = xmlFile.resolveSibling(xmlFile.getFileName().toString() + ".html");

        Source xslt = new StreamSource(SaxonProblem.class.getClassLoader().getResourceAsStream("xrechnung-html.xsl"));
        Source xml = new StreamSource(xmlFile.toFile());
        transformXml(xml, xslt, html);
    }

    public static void transformXml(Source xml, Source xslt, Path output) throws SaxonApiException, SAXException, IOException {
        Processor processor = new Processor(false);
        XsltCompiler compiler = processor.newXsltCompiler();
        XsltExecutable stylesheet = compiler.compile(xslt);
        Serializer out = processor.newSerializer(output.toFile());
        out.setOutputProperty(Serializer.Property.METHOD, "html");
        out.setOutputProperty(Serializer.Property.INDENT, "yes");
        Xslt30Transformer transformer = stylesheet.load30();
        transformer.transform(xml, out);
    }
}

解决方案

感谢 Martin Honnen 的评论和 Michael Kay 的回答,我有一个使用 UnparsedTextURIResolver 的解决方案。 感觉更像是一个黑客,但它有效并且比我以前的解决方法更好:

Processor processor = new Processor(false);
UnparsedTextURIResolver defaultUtur = processor.getUnderlyingConfiguration().getUnparsedTextURIResolver();
processor.getUnderlyingConfiguration().setUnparsedTextURIResolver(new UnparsedTextURIResolver() {
    @Override
    public Reader resolve(URI arg0, String arg1, Configuration arg2) throws XPathException {
        if (arg0.toString().endsWith("myfilename.css")) {
            InputStream css = SaxonProblem.class.getClassLoader().getResourceAsStream("myfilename.css");
            return new InputStreamReader(css);
        }
        return defaultUtur.resolve(arg0, arg1, arg2);
    }
});
//[...]

一些建议:

  • 使用带有classpath:方案的 URI(最近添加的,可能不是所有使用 URI 的路径都支持)

  • 使用配置注册一个UnparsedTextResolver Saxon 将把寻找资源的任务委托给这个解析器

  • 提供包含目录的名称作为样式表的参数,并使用resolve-uri()函数获取绝对 URI

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM