6

昨天甲骨文决定暂时关闭 java.sun.com。这让我搞砸了,因为 xalan 试图验证一些 XML 但无法检索 properties.dtd。

我正在使用 xalan 2.7.1 运行一些 XSL 转换,并且我不希望它验证任何内容。所以尝试像这样加载 XSL:

SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(true);
spf.setValidating(false);
XMLReader rdr = spf.newSAXParser().getXMLReader();      
Source xsl = new SAXSource(rdr, new InputSource(xslFilePath));  
Templates cachedXSLT  = factory.newTemplates(xsl);
Transformer transformer = cachedXSLT.newTransformer();         
transformer.transform(xmlSource, result);  

在 XSL 本身中,我执行以下操作:

  <xsl:variable name="entry" select="document(concat($prefix, $locale_part, $suffix))/properties/entry[@key=$key]"/>

此代码检索的 XML 在顶部具有以下定义:

<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="...

尽管上面的 java 代码指示解析器不验证,它仍然向 java.sun.com 发送请求。虽然 java.sun.com 不可用,但这会使转换失败并显示以下消息:

 Can not load requested doc: http://java.sun.com/dtd/properties.dtd

如何让 xalan 停止尝试验证从“文档”函数加载的 XML?

4

5 回答 5

3

请注意,如果 DTD 定义了您的 XML 文件所依赖的任何实体,则禁用 DTD 加载将导致解析失败。也就是说,要禁用 DTD 加载试试这个,它假设您使用的是 Java 附带的默认 Xerces。

    /*
     * Instantiate the SAXParser and set the features to prevent loading of an external DTD
     */
   SAXParser sp = SAXParserFactory.newInstance().newSAXParser();
   XMLReader xrdr = sp.getXMLReader();
   xrdr.setFeature("http://xml.org/sax/features/validation", false);
   xrdr.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

如果您真的需要 DTD,那么另一种选择是实现本地 XML 目录

    /*
     * Instantiate the SAXParser and add catalog support
     */
   SAXParser sp = SAXParserFactory.newInstance().newSAXParser();
   XMLReader xrdr = sp.getXMLReader();

   CatalogResolver cr = new CatalogResolver();
   xrdr.setEntityResolver(cr);

您必须向其提供适当的 DTD 和 XML 目录定义。这篇维基百科文章这篇文章很有帮助。

CatalogResolver 查看系统属性xml.catalog.files以确定要加载的目录。

于 2011-06-30T18:21:58.913 回答
3

文档提到即使不进行验证,解析器也可以读取 DTD,因为可能有必要使用 DTD 来解析(扩展)实体。

由于我无法控制 XML 文档,因此我无法使用nont 修改 XML 的选项

我设法通过破坏解析器来阻止拉入 DTD 文档的尝试,如下所示。

我的代码使用 DocumentBuilder 来返回一个 Document (= DOM),但根据 OP 的示例,XMLReader 也有一个方法setEntityResolver,因此同样的技术应该适用于此。

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false); // turns off validation
factory.setSchema(null);      // turns off use of schema
                              // but that's *still* not enough!
builder = factory.newDocumentBuilder();
builder.setEntityResolver(new NullEntityResolver()); // swap in a dummy resolver
return builder().parse(xmlFile); 

现在,这里是我的假解析器:无论要求什么,它都会返回一个空的 InputStream。

/** my resolver that doesn't */
private static class NullEntityResolver implements EntityResolver {

    public InputSource resolveEntity(String publicId, String systemId) 
    throws SAXException, IOException {
        // Message only for debugging / if you care
        System.out.println("I'm asked to resolve: " + publicId + " / " + systemId);
        return new InputSource(new ByteArrayInputStream(new byte[0]));
    }

}

或者,您的假解析器可以返回读取为本地资源或其他任何内容的实际文档流。

于 2012-02-22T16:15:52.907 回答
1

尝试使用 setFeature SAXParserFactory

试试这个:

SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setValidating(false);
spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

我认为这应该足够了,否则尝试设置一些其他功能:

spf.setFeature("http://xml.org/sax/features/validation", false);
spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
spf.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
于 2011-06-30T18:22:27.523 回答
0

我刚刚从 XML 中删除了 doctype 声明,因为没有其他方法起作用。当我解决它时,我会试试这个: http: //www.sagehill.net/docbookxsl/UseCatalog.html#UsingCatsXalan

于 2011-07-07T20:08:12.757 回答
0

很抱歉发布了 necroposting,但我找到了一个真正有效的解决方案,并决定我应该分享它。

1. 出于某种原因,setValidating(false) 不起作用。在某些情况下,它仍会下载外部 DTD 文件。为了防止这种情况,您应该按照此处的建议附加一个自定义 EntityResolver :

XMLReader rdr = spf.newSAXParser().getXMLReader();      
rdr.setEntityResolver(new MyCustomEntityResolver());

将为每个外部实体请求调用 EntityResolver。返回null将不起作用,因为此后框架仍会从 Internet 下载文件。相反,您可以返回一个有效 DTD 的空流,如下所示

private class MyCustomEntityResolver implements EntityResolver {
    public InputSource resolveEntity(String publicId, String systemId) {
        return new InputSource(new StringReader(""));
    }
}

2. 您将 setValidating(false) 告诉读取您的 XSLT 代码的 SAX 解析器。也就是说,它不会验证您的 XSLT。当它遇到一个 document() 函数时,它使用另一个解析器加载链接的 XML 文件,该解析器仍然对其进行验证,并下载外部实体。要处理这个问题,您应该将自定义 URIResolver 附加到转换器:

Transformer transformer = cachedXSLT.newTransformer();
transformer.setURIResolver(new MyCustomURIResolver());

当遇到 document() 函数时,转换器将调用您的 URIResolver 实现。您的实现必须为传递的 URI 返回一个 Source。最简单的方法是按照此处的建议返回 StreamSource 。但是在您的情况下,您应该自己解析文档,使用您已经拥有的自定义 SAXParser(或每次创建一个新的)来防止验证和外部请求。

private class MyCustomURIResolver implements URIResolver {
    public Source resolve(String href, String base) {
        return new SAXSource(rdr,new InputSource(href));
    }
}

因此,您必须在代码中实现两个自定义接口。

于 2015-12-31T11:13:58.773 回答