【问题标题】:TransformerFactory - avoiding network lookups to verify DTDsTransformerFactory - 避免网络查找以验证 DTD
【发布时间】:2013-09-23 18:47:42
【问题描述】:

我需要为 XML 文档的离线转换编程。 在使用以下内容加载原始 XML 文件时,我已经能够停止 DTD 网络查找:

DocumentBuilderFactory factory;

factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(true);
factory.setFeature("http://xml.org/sax/features/namespaces", false);
factory.setFeature("http://xml.org/sax/features/validation", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
// open up the xml document
docbuilder = factory.newDocumentBuilder();
doc = docbuilder.parse(new FileInputStream(m_strFilePath));

但是,我无法将其应用于 TransformerFactory 对象。 DTD 在本地可用,但我不知道如何指示转换器查看本地文件,而不是尝试进行网络查找。

据我所知,transformer 需要这些文件才能正确进行转换。

有关信息,我正在将 MusicXML 文档从 Partwise 转换为 Timewise。

您可能已经猜到,XSLT 不是我的强项(远非如此)。

我是否需要修改 XSLT 文件以引用本地文件,或者可以以不同的方式完成吗?


除了下面的 cmets,这里是 xsl 文件的摘录。这是我看到的唯一一个引用外部文件的地方:

<!--
  XML output, with a DOCTYPE refering the timewise DTD.
  Here we use the full Internet URL. 
-->
<xsl:output method="xml" indent="yes" encoding="UTF-8"
    omit-xml-declaration="no" standalone="no"
    doctype-system="http://www.musicxml.org/dtds/timewise.dtd"
    doctype-public="-//Recordare//DTD MusicXML 2.0 Timewise//EN" />

上述技术是否也适用于此?

DTD 文件包含对许多 MOD 文件的引用,如下所示:

<!ENTITY % layout PUBLIC
    "-//Recordare//ELEMENTS MusicXML 2.0 Layout//EN"
    "layout.mod">

我想这些文件也会依次导入。

【问题讨论】:

  • 您可以尝试将 docs.oracle.com/javase/7/docs/api/javax/xml/transform/… 设置为加载本地 DTD 的解析器实现。
  • 感谢@MartinHonnen,我查看了您提供的链接,发现它适用于导入和包含。那么 xsl 没有任何明确的包含。我将在下面摘录来说明我的意思。
  • “我想这些文件也会被依次导入”——你验证了吗?据我了解,&lt;xsl:output&gt; 不会导致样式表处理器实际加载 DTD,它只会影响在将其序列化为 XML 时添加到输出树的&lt;!DOCTYPE&gt;
  • 坦率地说@IanRoberts,我不确定。我所知道的是,如果我断开网络连接,它会抛出一个异常,说它无法连接到“www.musicxml.org”。当我尝试实现 URIResolver 时,它甚至没有被调用。我在这里有点不知所措!

标签: java xml xslt musicxml


【解决方案1】:

好的,这是适合我的答案。

第一步:加载原始文档,关闭工厂内的验证和dtd加载。

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// stop the network loading of DTD files
factory.setValidating(false);
factory.setNamespaceAware(true);
factory.setFeature("http://xml.org/sax/features/namespaces", false);
factory.setFeature("http://xml.org/sax/features/validation", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
// open up the xml document
DocumentBuilder docbuilder = factory.newDocumentBuilder();
Document doc = docbuilder.parse(new FileInputStream(m_strFilePath));

第二步:现在我已经在内存中获得了文档......并且在检测到我需要转换它之后 -

TransformerFactory transformfactory = TransformerFactory.newInstance();
Templates xsl = transformfactory.newTemplates(new StreamSource(new FileInputStream((String)m_XslFile)));
Transformer transformer = xsl.newTransformer();
Document newdoc = docbuilder.newDocument();
Result XmlResult = new DOMResult(newdoc);
// now transform
transformer.transform(
        new DOMSource(doc.getDocumentElement()),
        XmlResult);

我需要这样做,因为我之后有进一步的处理,并且不希望输出到文件和重新加载的开销。

小解释:

诀窍是使用已关闭所有验证功能的原始 DOM 对象。你可以在这里看到:

transformer.transform(
        new DOMSource(doc.getDocumentElement()),  // <<-----
        XmlResult);

已在网络访问关闭的情况下进行了测试。 所以我知道没有更多的网络查找。

但是,如果 DTD、MOD 等在本地可用,那么根据建议,使用 EntityResolver 就是答案。这将再次应用于原始 docbuilder 对象。

我现在有一个存储在 newdoc 中的转换后的文档,可以使用了。

我希望这对其他人有所帮助。

【讨论】:

    【解决方案2】:

    您可以使用 Apache xml-commons-resolver 之类的库并编写目录文件以将 Web URL 映射到相关文件的本地副本。要将此目录连接到转换器机制,您需要使用 SAXSource 而不是 StreamSource 作为样式表的源:

    SAXSource styleSource = new SAXSource(new InputSource("file:/path/to/stylesheet.xsl"));
    CatalogResolver resolver = new CatalogResolver();
    styleSource.getXMLReader().setEntityResolver(resolver);
    TransformerFactory tf = TransformerFactory.newInstance();
    tf.setURIResolver(resolver);
    Transformer transformer = tf.newTransformer(styleSource);
    

    【讨论】:

      【解决方案3】:

      在 Java 中执行此操作的常用方法是使用 LSResourceResolver 将系统 ID(和/或公共 ID)解析为本地文件。这记录在http://docs.oracle.com/javase/7/docs/api/org/w3c/dom/ls/LSResourceResolver.html。除了标准的 Java XML 解析器功能之外,您不需要任何东西来实现此功能。

      【讨论】:

        猜你喜欢
        • 2018-10-04
        • 2020-07-28
        • 2018-08-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-01-22
        • 1970-01-01
        • 2012-11-28
        相关资源
        最近更新 更多