Certifique DocumentBuilder.parse ignorar referências DTD
Pergunta
Quando eu analisar meu arquivo xml (variável f) neste método, eu recebo um erro
C: \ Documents and Settings \ Joe \ Desktop \ aicpcudev \ OnlineModule \ map.dtd (O sistema não pode encontrar o caminho especificado)
Eu sei que eu não tenho o DTD, nem eu precisar dele. Como posso analisar esse objeto File em um objeto Document, ignorando erros de referência 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;
}
Solução
Uma abordagem semelhante ao 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;
}
}
});
Eu achei que simplesmente devolver um InputSource vazio funcionou tão bem?
Outras dicas
Tente configurar recursos na 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();
...
Em última análise, acho que as opções são específicas para a implementação de analisador. Aqui está alguma documentação para Xerces2 se isso ajuda.
Eu encontrei um problema onde o arquivo DTD estava no arquivo jar junto com o XML. Eu resolvi o problema com base nos exemplos aqui, como segue: -
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;
}
}
});
Eu sei que eu não tenho o DTD, nem eu precisar dele.
Estou desconfiado desta declaração; se seu documento contém quaisquer referências de entidade? Se assim for, você definitivamente precisa o DTD.
De qualquer forma, a forma usual de impedir que isso aconteça é usar um catálogo de XML para definir um caminho local para "map.dtd".
aqui está outro usuário que tem o mesmo problema: http: // fóruns. sun.com/thread.jspa?threadID=284209&forumID=34
ddssot utilizador nesse post diz
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;
}
});
O usuário menciona ainda "Como você pode ver, quando o analisador atinge o DTD, o resolvedor entidade é chamado. Eu reconheço meu DTD com o seu ID específico e retornar um documento XML vazio em vez do real DTD, parando todas as validação. .. "
Espero que isso ajude.
XML Fonte (Com 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>
implementação Java DOM para aceitar acima XML como cordas e remoção de declaração 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 Destino (Sem DTD)
<MYACCSERVICE>
<REQ_PAYLOAD>
<ACCOUNT>1234567890</ACCOUNT>
<BRANCH>001</BRANCH>
<CURRENCY>USD</CURRENCY>
<TRANS_REFERENCE>201611100000777</TRANS_REFERENCE>
</REQ_PAYLOAD>
</MYACCSERVICE>