如何告诉xalan不要validation使用“document”函数检索的XML?

昨天甲骨文决定取消java.sun.com一段时间。 这为我搞砸了,因为xalan试图validation一些XML但无法检索properties.dtd。

我正在使用xalan 2.7.1来运行一些XSL转换,我不希望它validation任何东西。 所以尝试像这样加载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本身,我做这样的事情:

   

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

   <entry key="... 

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

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

如何让xalan停止尝试validation从“document”函数加载的XML?

文档提到即使没有validation,解析器也可能读取DTD,因为可能需要使用DTD来解析(扩展)实体。

由于我无法控制XML文档, 因此我无法使用修改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])); } } 

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

请注意,如果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以确定要加载的目录。

尝试在SAXParserFactory上使用setFeature。

尝试这个:

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

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

 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); 

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

对不起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)到SAX解析器,它读取您的XSLT代码。 也就是说,它不会validation您的XSLT。 遇到document()函数时,它会使用另一个仍然validation它的解析器加载链接的XML文件,并下载外部实体。 要处理此问题,您应该将自定义URIResolver附加到转换器:

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

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

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

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