使DocumentBuilder.parse忽略DTD引用


81

当我使用这种方法解析xml文件(变量f)时,出现错误

C:\ Documents and Settings \ joe \ Desktop \ aicpcudev \ OnlineModule \ map.dtd(系统找不到指定的路径)

我知道我没有dtd,也不需要它。如何在忽略DTD参考错误的同时将此File对象解析为Document对象?

private static Document getDoc(File f, String docId) throws Exception{
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder db = dbf.newDocumentBuilder();
    Document doc = db.parse(f);


    return doc;
}

1
我相信jt对于这个问题有最好的答案。
simgineer

Answers:


58

@anjanb建议的方法类似的方法

    builder.setEntityResolver(new EntityResolver() {
        @Override
        public InputSource resolveEntity(String publicId, String systemId)
                throws SAXException, IOException {
            if (systemId.contains("foo.dtd")) {
                return new InputSource(new StringReader(""));
            } else {
                return null;
            }
        }
    });

我发现仅返回一个空的InputSource也可以吗?


4
在DocumentBuilderFactory上设置功能对我有用。这篇文章中的解决方案不起作用。
凯梅切尔

4
即使我以为我没有使用SAX,这对我也非常有效
devnull69 2013年

不幸的是,这对我没有用。我仍然有错误。@jt为我做到了。
Nils-o-mat


135

尝试在DocumentBuilderFactory上设置功能:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

dbf.setValidating(false);
dbf.setNamespaceAware(true);
dbf.setFeature("http://xml.org/sax/features/namespaces", false);
dbf.setFeature("http://xml.org/sax/features/validation", false);
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

DocumentBuilder db = dbf.newDocumentBuilder();
...

最终,我认为这些选项特定于解析器实现。如果有帮助,这里是Xerces2的一些文档


21
最后一个(load-external-dtd)为我成功了-谢谢。
Amarghosh,

1
尝试此操作时,我得到了DOMException:NAMESPACE_ERR:试图以一种与名称空间有关的方式来创建或更改对象。。我通过dbf.setNamespaceAware(true);
Tim Van Laer

只是让您知道,最后一个功能设置(如@Amarghosh所述)可以与SAXParserFactory一起使用。
亚历克西斯·勒克莱尔

1
对我来说,load-external-dtd设置就足够了。
克里斯(Chris)2015年

使用上述所有功能也会使代码失败。仅使用最后两个功能(非验证)即可使我的代码正常工作。
Purus

5

我发现一个问题,其中DTD文件与XML一起位于jar文件中。我根据此处的示例解决了该问题,如下所示:-

DocumentBuilder db = dbf.newDocumentBuilder();
db.setEntityResolver(new EntityResolver() {
    public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
        if (systemId.contains("doc.dtd")) {
             InputStream dtdStream = MyClass.class
                     .getResourceAsStream("/my/package/doc.dtd");
             return new InputSource(dtdStream);
         } else {
             return null;
         }
      }
});

4

源XML(带DTD)

<!DOCTYPE MYSERVICE SYSTEM "./MYSERVICE.DTD">
<MYACCSERVICE>
   <REQ_PAYLOAD>
      <ACCOUNT>1234567890</ACCOUNT>
      <BRANCH>001</BRANCH>
      <CURRENCY>USD</CURRENCY>
      <TRANS_REFERENCE>201611100000777</TRANS_REFERENCE>
   </REQ_PAYLOAD>
</MYACCSERVICE>

Java DOM实现,用于接受上述XML作为字符串并删除DTD声明

public Document removeDTDFromXML(String payload) throws Exception {

    System.out.println("### Payload received in XMlDTDRemover: " + payload);

    Document doc = null;
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    try {

        dbf.setValidating(false);
        dbf.setNamespaceAware(true);
        dbf.setFeature("http://xml.org/sax/features/namespaces", false);
        dbf.setFeature("http://xml.org/sax/features/validation", false);
        dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
        dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

        DocumentBuilder db = dbf.newDocumentBuilder();

        InputSource is = new InputSource();
        is.setCharacterStream(new StringReader(payload));
        doc = db.parse(is); 

    } catch (ParserConfigurationException e) {
        System.out.println("Parse Error: " + e.getMessage());
        return null;
    } catch (SAXException e) {
        System.out.println("SAX Error: " + e.getMessage());
        return null;
    } catch (IOException e) {
        System.out.println("IO Error: " + e.getMessage());
        return null;
    }
    return doc;

}

目标XML(无DTD)

<MYACCSERVICE>
   <REQ_PAYLOAD>
      <ACCOUNT>1234567890</ACCOUNT>
      <BRANCH>001</BRANCH>
      <CURRENCY>USD</CURRENCY>
      <TRANS_REFERENCE>201611100000777</TRANS_REFERENCE>
   </REQ_PAYLOAD>
</MYACCSERVICE> 

2

我知道我没有dtd,也不需要它。

我对此说法表示怀疑;您的文档中是否包含任何实体引用?如果是这样,您肯定需要DTD。

无论如何,防止这种情况发生的通常方法是使用XML目录来定义“ map.dtd”的本地路径。


2

这是另一个遇到相同问题的用户:http : //forums.sun.com/thread.jspa?threadID=284209&forumID=34

该帖子上的用户ddssot说

myDocumentBuilder.setEntityResolver(new EntityResolver() {
          public InputSource resolveEntity(java.lang.String publicId, java.lang.String systemId)
                 throws SAXException, java.io.IOException
          {
            if (publicId.equals("--myDTDpublicID--"))
              // this deactivates the open office DTD
              return new InputSource(new ByteArrayInputStream("<?xml version='1.0' encoding='UTF-8'?>".getBytes()));
            else return null;
          }
});

用户进一步提到“如您所见,当解析器命中DTD时,将调用实体解析器。我识别出具有特定ID的DTD并返回一个空的XML文档而不是真实的DTD,从而停止所有验证...”

希望这可以帮助。


0

我正在使用sonarqube,并且sonarlint for eclipse显示了我应该在不解析外部数据的情况下解析不受信任的XML(鱿鱼:S2755)

我设法使用以下方法解决它:

    factory = DocumentBuilderFactory.newInstance();

    factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

    // If you can't completely disable DTDs, then at least do the following:
    // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities
    // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities
    // JDK7+ - http://xml.org/sax/features/external-general-entities
    factory.setFeature("http://xml.org/sax/features/external-general-entities", false);

    // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-parameter-entities
    // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities
    // JDK7+ - http://xml.org/sax/features/external-parameter-entities
    factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

    // Disable external DTDs as well
    factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

    // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
    factory.setXIncludeAware(false);
    factory.setExpandEntityReferences(false);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.