Hacer que DocumentBuilder.parse ignore las referencias DTD


81

Cuando analizo mi archivo xml (variable f) en este método, aparece un error

C: \ Documents and Settings \ joe \ Desktop \ aicpcudev \ OnlineModule \ map.dtd (El sistema no puede encontrar la ruta especificada)

Sé que no tengo el dtd ni lo necesito. ¿Cómo puedo analizar este objeto Archivo en un objeto Documento sin tener en cuenta los errores de referencia de DTD?

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
Creo que jt tiene la mejor respuesta para esta pregunta.
Simgineer

Respuestas:


58

Un enfoque similar al sugerido por @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;
            }
        }
    });

Descubrí que simplemente devolver un InputSource vacío funcionaba igual de bien.


4
Configurar las funciones en DocumentBuilderFactory funcionó para mí. La solución en esta publicación no funcionó.
Kai Mechel

4
Esto también funcionó perfectamente para mí, aunque pensé que no usaba SAX
devnull69

Lamentablemente, esto no funcionó para mí. Todavía tengo el error. @jt lo hizo por mí.
Nils-o-mat

Gracias por la solución, creo que este es el enfoque recomendado por org.xml . Parece que hay mucho material sobre este tema. ver xerces.apache.org/xml-commons/components/resolver/… , o en.wikipedia.org/wiki/XML_Catalog y el javadoc saxproject.org/apidoc/org/xml/sax/EntityResolver.html y saxproject.org/ apidoc / org / xml / sax / ext / EntityResolver2.html
aliopi

135

Intente configurar funciones en 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();
...

En última instancia, creo que las opciones son específicas de la implementación del analizador. Aquí hay algo de documentación para Xerces2 si eso ayuda.


21
el último ( load-external-dtd) me hizo el truco - gracias.
Amarghosh

1
Al intentar esto, obtuve una DOMException: NAMESPACE_ERR: se intenta crear o cambiar un objeto de una manera incorrecta con respecto a los espacios de nombres. . Arreglé esto condbf.setNamespaceAware(true);
Tim Van Laer

Solo para informarle, la última configuración de características (como lo indica @Amarghosh) funciona muy bien con SAXParserFactory.
Alexis Leclerc

1
Para mí el load-external-dtdescenario fue suficiente.
chris

El uso de todas las funciones anteriores también hace que el código falle. El solo uso de las dos últimas funciones (no validantes) hace que mi código funcione.
Purús

5

Encontré un problema en el que el archivo DTD estaba en el archivo jar junto con el XML. Resolví el problema en base a los ejemplos aquí, de la siguiente manera: -

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 de origen (con 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>

Implementación de DOM de Java para aceptar XML anterior como cadena y eliminar la declaración 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 de destino (sin DTD)

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

2

Sé que no tengo el dtd ni lo necesito.

Sospecho de esta declaración; ¿Su documento contiene referencias a entidades? Si es así, definitivamente necesita el DTD.

De todos modos, la forma habitual de evitar que esto suceda es utilizando un catálogo XML para definir una ruta local para "map.dtd".


2

aquí hay otro usuario que tuvo el mismo problema: http://forums.sun.com/thread.jspa?threadID=284209&forumID=34

el usuario ddssot en esa publicación dice

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

El usuario menciona además: "Como puede ver, cuando el analizador llega al DTD, se llama al solucionador de entidades. Reconozco mi DTD con su ID específico y devuelvo un documento XML vacío en lugar del DTD real, deteniendo toda validación ..."

Espero que esto ayude.


0

Estoy trabajando con sonarqube, y sonarlint para eclipse me mostró que XML que no es de confianza debe analizarse sin resolver datos externos (squid: S2755)

Logré resolverlo usando:

    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);
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.