我从服务器获取任意XML并使用此Java代码解析它:
String xmlStr; // arbitrary XML input
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource is = new InputSource(new StringReader(xmlStr));
return builder.parse(is);
}
catch (SAXException | IOException | ParserConfigurationException e) {
LOGGER.error("Failed to parse XML.", e);
}
每隔一段时间,XML输入就包含一些未知的实体引用,如
,并且失败并显示错误,例如org.xml.sax.SAXParseException: The entity "nbsp" was referenced, but not declared.
我可以通过预处理原始xmlStr
并在解析之前翻译所有有问题的实体引用来解决此问题。这是一个有效的虚拟实现:
protected static String translateEntityReferences(String xml) {
String newXml = xml;
Map<String, String> entityRefs = new HashMap<>();
entityRefs.put(" ", " ");
entityRefs.put("«", "«");
entityRefs.put("»", "»");
// ... and 250 more...
for(Entry<String, String> er : entityRefs.entrySet()) {
newXml = newXml.replace(er.getKey(), er.getValue());
}
return newXml;
}
然而,这真的不能令人满意,因为are a huge number of entity references我不希望所有硬编码到我的Java类中。
有没有简单的方法来教授DocumentBuilder的整个字符实体引用列表?
答案 0 :(得分:1)
如果您可以更改代码以使用StAX而不是DOM,那么简单的解决方案是使用设置为XMLInputFactory
的{{1}}属性IS_REPLACING_ENTITY_REFERENCES
。
false
输出:
public static void main(String[] args) throws Exception
{
String doc = "<doc> </doc>";
ByteArrayInputStream is = new ByteArrayInputStream(doc.getBytes());
XMLInputFactory xif = XMLInputFactory.newFactory();
xif.setProperty(javax.xml.stream.XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
XMLStreamReader xr = xif.createXMLStreamReader(is);
while(xr.hasNext())
{
int t = xr.getEventType();
switch(t) {
case XMLEvent.ENTITY_REFERENCE:
System.out.println("Entity: "+ xr.getLocalName());
break;
case XMLEvent.START_DOCUMENT:
System.out.println("Start Document");
break;
case XMLEvent.START_ELEMENT:
System.out.println("Start Element: " + xr.getLocalName());
break;
case XMLEvent.END_DOCUMENT:
System.out.println("End Document");
break;
case XMLEvent.END_ELEMENT:
System.out.println("End Element: " + xr.getLocalName());
break;
default:
System.out.println("Other: ");
break;
}
xr.next();
}
}
但是如果你真的需要内存中的完整DOM树,那么你的代码中可能需要重写太多。
我花了一个小时来跟踪DOM实现,但找不到任何方法来从Start Document
Start Element: doc
Entity: nbsp null
End Element: doc
读取DOM解析器。
在代码中还有证据表明内部DOM解析器实现有一个类似于XMLStreamReader
的选项,但我找不到任何方法从外部设置它。