Java,xml,XSLT:防止DTD验证

5
我使用Java(6)XML-Api对来自网络的html文档进行xslt转换。该文档是格式良好的xhtml,因此包含有效的DTD规范(<! DOCTYPE html PUBLIC“ -//W3C // DTD XHTML 1.0 Transitional // EN”“ http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> )。 现在出现了一个问题:转换时,XSLT处理器尝试下载DTD,但w3服务器通过HTTP 503错误拒绝此操作(由于w3的带宽限制)。

我如何防止XSLT处理器下载DTD?我不需要验证我的输入文档。

源代码:

import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

--

   String xslt = "<?xml version=\"1.0\"?>"+
   "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">"+
   "    <xsl:output method=\"text\" />"+          
   "    <xsl:template match=\"//html/body//div[@id='bodyContent']/p[1]\"> "+
   "        <xsl:value-of select=\".\" />"+
   "     </xsl:template>"+
   "     <xsl:template match=\"text()\" />"+
   "</xsl:stylesheet>";

   try {
   Source xmlSource = new StreamSource("http://de.wikipedia.org/wiki/Right_Livelihood_Award");
   Source xsltSource = new StreamSource(new StringReader(xslt));
   TransformerFactory ft = TransformerFactory.newInstance();

   Transformer trans = ft.newTransformer(xsltSource);

   trans.transform(xmlSource, new StreamResult(System.out));
   }
   catch (Exception e) {
     e.printStackTrace();
   }

我在SO上读到了以下问题,但它们都使用另一个XML-API:

谢谢!

5个回答

5

最近我在使用JAXB解组XML时遇到了这个问题。答案是从XmlReader和InputSource创建一个SAXSource,然后将其传递给JAXB UnMarshaller的unmarshal()方法。为了避免加载外部DTD,我在XmlReader上设置了自定义EntityResolver。

SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xmlr = sp.getXMLReader();
xmlr.setEntityResolver(new EntityResolver() {
    public InputSource resolveEntity(String pid, String sid) throws SAXException {
        if (sid.equals("your remote dtd url here"))
            return new InputSource(new StringReader("actual contents of remote dtd"));
        throw new SAXException("unable to resolve remote entity, sid = " + sid);
    } } );
SAXSource ss = new SAXSource(xmlr, myInputSource);

按照现有的写法,如果这个自定义实体解析器被要求解析除你想解析的实体之外的任何实体,它都会抛出一个异常。如果你只想让它继续加载远程实体,请删除 "throws" 行。


1
以防万一有人遇到相同的问题:这会引导您走向正确的方向(这就是为什么我接受了答案)。如果您不想返回DTD,也可以返回一个空的。 - theomega
1
请修正大小写:'XmlReader' 应为 'XMLReader'。 - wau

4
尝试在您的DocumentBuilderFactory中设置一个功能:
URL url = new URL(urlString);
InputStream is = url.openStream();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
DocumentBuilder db;
db = dbf.newDocumentBuilder();
Document result = db.parse(is);

目前我在使用XSLT(2)时遇到了同样的问题,即在调用document函数分析外部XHTML页面时。


2

之前的回答为我提供了解决方案,但对我来说并不明显,因此这里提供一个完整的解决方案:

private void convert(InputStream xsltInputStream, InputStream srcInputStream, OutputStream destOutputStream) throws SAXException, ParserConfigurationException,
        TransformerFactoryConfigurationError, TransformerException, IOException {
    //create a parser with a fake entity resolver to disable DTD download and validation
    XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
    xmlReader.setEntityResolver(new EntityResolver() {
        public InputSource resolveEntity(String pid, String sid) throws SAXException {
            return new InputSource(new ByteArrayInputStream(new byte[] {}));
        }
    });
    //create the transformer
    Source xsltSource = new StreamSource(xsltInputStream);
    Transformer transformer = TransformerFactory.newInstance().newTransformer(xsltSource);
    //create the source for the XML document which uses the reader with fake entity resolver
    Source xmlSource = new SAXSource(xmlReader, new InputSource(srcInputStream));
    transformer.transform(xmlSource, new StreamResult(destOutputStream));
}

-1

如果你使用

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

你可以尝试使用以下代码禁用DTD验证:

 dbf.setValidating(false);

看看 Chris 的回答,它完全一样。 - theomega

-2

你需要使用javax.xml.parsers.DocumentBuilderFactory

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource src = new InputSource("http://de.wikipedia.org/wiki/Right_Livelihood_Award")
Document xmlDocument = builder.parse(src.getByteStream());
DOMSource source = new DOMSource(xmlDocument);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer(xsltSource);
transformer.transform(source, new StreamResult(System.out));

谢谢您的回答,但是这段代码实际上会抛出相同的异常: java.io.IOException: Server returned HTTP response code: 503 for URL: http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd 为了让它正常工作,您必须将第5行中的 src.getByteStream() 更改为 src,但仍然会出现相同的异常。 - theomega
1
这并没有改变什么。您可以在从流源进行转换期间解析文档,也可以在转换为DOMSource之前解析文档,但无论哪种方式,都会出现缺少DTD异常。因此,这个“解决方案”解决不了任何问题,只会误导人。 - mvmn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接