Rendre DocumentBuilder.parse ignorer les références DTD
Question
Lorsque j'analyse mon fichier xml (variable f) dans cette méthode, j'obtiens une erreur
C: \ Documents and Settings \ joe \ Desktop \ aicpcudev \ OnlineModule \ map.dtd (le système ne peut pas trouver le chemin spécifié)
Je sais que je n’ai pas le dtd, et n’en ai pas besoin. Comment analyser cet objet File dans un objet Document en ignorant les erreurs de référence 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;
}
La solution
Une approche similaire à celle suggérée par @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;
}
}
});
J'ai trouvé que renvoyer simplement un InputSource vide fonctionnait aussi bien?
Autres conseils
Essayez de définir des fonctionnalités sur 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 définitive, je pense que les options sont spécifiques à la mise en œuvre de l’analyseur. Voici une documentation pour Xerces2 si cela vous aide.
J'ai trouvé un problème où le fichier DTD était dans le fichier jar avec le fichier XML. J'ai résolu le problème en me basant sur les exemples ci-dessous:
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;
}
}
});
Je sais que je n'ai pas le dtd, et j'en ai pas besoin.
Je me méfie de cette déclaration; votre document contient-il des références d'entités? Si tel est le cas, vous avez certainement besoin de la DTD.
Quoi qu'il en soit, le moyen habituel d'éviter ce problème consiste à utiliser un catalogue XML pour définir un chemin d'accès local pour "map.dtd".
voici un autre utilisateur qui a le même problème: http: // forums. sun.com/thread.jspa?threadID=284209&forumID=34
l'utilisateur ddssot sur cet article dit
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;
}
});
L'utilisateur mentionne ensuite "Comme vous pouvez le constater, lorsque l'analyseur frappe la DTD, le résolveur d'entité est appelé. Je reconnais ma DTD avec son ID spécifique et renvoie un document XML vide à la place de la DTD réelle, en interrompant toute validation ... "
J'espère que cela vous aidera.
XML source (avec 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>
Implémentation DOM Java pour accepter le XML ci-dessus en tant que chaîne et supprimer la déclaration 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 destination (sans DTD)
<MYACCSERVICE>
<REQ_PAYLOAD>
<ACCOUNT>1234567890</ACCOUNT>
<BRANCH>001</BRANCH>
<CURRENCY>USD</CURRENCY>
<TRANS_REFERENCE>201611100000777</TRANS_REFERENCE>
</REQ_PAYLOAD>
</MYACCSERVICE>