[java] DocumentBuilder.parse가 DTD 참조를 무시하도록합니다.

이 메서드에서 내 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;
}



답변

@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를 반환하는 것만으로도 잘 작동한다는 것을 알았습니다.


답변

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에 대한 몇 가지 문서가 있습니다 .


답변

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


답변

소스 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>

위의 XML을 문자열로 받아들이고 DTD 선언을 제거하기위한 Java DOM 구현

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>


답변

dtd가없고 필요하지 않다는 것을 알고 있습니다.

나는이 진술이 의심 스럽습니다. 문서에 엔티티 참조가 포함되어 있습니까? 그렇다면 반드시 DTD가 필요합니다.

어쨌든 이것을 방지하는 일반적인 방법은 XML 카탈로그를 사용하여 “map.dtd”에 대한 로컬 경로를 정의하는 것입니다.


답변

동일한 문제가 발생한 다른 사용자가 있습니다. 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를 인식하고 실제 DTD 대신 빈 XML 문서를 반환하여 모든 유효성 검사를 중지합니다 …”

도움이 되었기를 바랍니다.


답변

나는 sonarqube로 작업하고 있으며 eclipse에 대한 sonarlint는 신뢰할 수없는 XML을 외부 데이터를 확인하지 않고 구문 분석해야한다는 것을 보여주었습니다 (squid : 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);