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

时间:2011-06-30 17:59:53

标签: java xml validation xalan

昨天甲骨文决定取消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代码指示解析器不是VALIDATE,但它仍然向java.sun.com发送请求。当java.sun.com不可用时,这会使转换失败并显示消息:

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

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

5 个答案:

答案 0 :(得分: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目录定义。这Wikipedia Articlethis article很有帮助。

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

答案 1 :(得分:3)

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

由于我无法控制XML文档,因此我无法使用nont's option of modifying the 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]));
    }

}

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

答案 2 :(得分:1)

尝试在SAXParserFactory上使用setFeature。

试试这个:

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

对于necroposting很抱歉,但我找到了一个实际可行的解决方案,并决定我应该分享它。

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

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

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

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

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

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

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

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

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