【问题标题】:Java, xml, XSLT: Prevent DTD-ValidationJava、xml、XSLT:防止 DTD 验证
【发布时间】:2010-12-07 01:35:29
【问题描述】:

我使用 Java (6) XML-Api 对来自网络的 html 文档应用 xslt 转换。该文档是格式良好的 xhtml,因此包含有效的 DTD-Spec (<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">)。 现在出现了一个问题:转换时 XSLT-Processor 尝试下载 DTD,而 w3-server 通过 HTTP 503 错误(由于 w3 的Bandwith Limitation)拒绝了这一点。

如何防止 XSLT-Processor 下载 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:

谢谢!

【问题讨论】:

    标签: java xml xslt


    【解决方案1】:

    之前的答案让我找到了一个解决方案,但对我来说并不明显,所以这里是一个完整的答案:

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

    【讨论】:

      【解决方案2】:

      我最近在使用 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”行。

      【讨论】:

      • 以防万一有人遇到同样的问题:这会导致正确的方向(这就是我接受答案的原因)。如果不想返回DTD,也可以返回一个空的。
      • 请修正大小写:'XmlReader' 应该是 'XMLReader'
      【解决方案3】:

      如果你使用

      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
      

      您可以尝试使用以下代码禁用 dtd 验证:

       dbf.setValidating(false);
      

      【讨论】:

      • 看克里斯的回答,一模一样。
      【解决方案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);
      

      现在我在调用文档函数分析外部 XHTML 页面时在 XSLT(2) 中遇到了同样的问题。

      【讨论】:

        【解决方案5】:

        您需要使用 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 才能使其正常工作,但仍然存在同样的例外。
        • 这没有任何改变。您可以在从流源转换到 DOMSource 之前的转换过程中解析文档,但无论哪种方式都会发生丢失 DTD 异常。所以这个“解决方案”解决不了任何问题,只会误导。
        猜你喜欢
        • 2010-11-22
        • 1970-01-01
        • 1970-01-01
        • 2016-09-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多