我正在解析(很多)包含实体引用的XML文件,我事先并不知道(不能改变这个事实)。
例如:
xml = "<tag>I'm content with &funny; &entity; &references;.</tag>"
当我尝试使用以下代码解析它时:
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
final DocumentBuilder db = dbf.newDocumentBuilder();
final InputSource is = new InputSource(new StringReader(xml));
final Document d = db.parse(is);
我得到以下例外:
org.xml.sax.SAXParseException: The entity "funny" was referenced, but not declared.
但是,我想要实现的是,解析器用空字符串''替换未声明的每个实体(解析器未知)。 或者更好的是,有没有办法将地图传递给解析器,如:
Map<String,String> entityMapping = ...
entityMapping.put("funny","very");
entityMapping.put("entity","important");
entityMapping.put("references","stuff");
这样我就可以做到以下几点:
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
final DocumentBuilder db = dbf.newDocumentBuilder();
final InputSource is = new InputSource(new StringReader(xml));
db.setEntityResolver(entityMapping);
final Document d = db.parse(is);
如果我使用这个示例代码从文档中获取文本,我应该收到:
I'm content with very important stuff.
有什么建议吗?当然,我已经很乐意用空字符串替换未知实体。
谢谢,
答案 0 :(得分:3)
StAX API支持此功能。看一下XMLInputFactory,它有一个runtime property,它决定了内部实体是否被扩展或留在原地。如果设置为false
,则StAX事件流将包含EntityReference
的实例,以表示未展开的实体。
如果你仍然想要一个DOM作为最终结果,你可以将它们链接在一起:
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
inputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
String xml = "my xml";
StringReader xmlReader = new StringReader(xml);
XMLEventReader eventReader = inputFactory.createXMLEventReader(xmlReader);
StAXSource source = new StAXSource(eventReader);
DOMResult result = new DOMResult();
transformer.transform(source, result);
Node document = result.getNode();
在这种情况下,生成的DOM将包含与文本节点混合的org.w3c.dom.EntityReference
节点。然后,您可以根据需要处理这些内容。
答案 1 :(得分:2)
由于您的XML输入似乎是以字符串形式提供的,您是否可以使用正则表达式替换进行简单的预处理?
xml = "...";
/* replace entities before parsing */
for (Map.Entry<String,String> entry : entityMapping.entrySet()) {
xml = xml.replaceAll("&" + entry.getKey() + ";", entry.getValue());
}
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
...
非常hacky,您可能需要花费一些额外的努力来确保regexp只匹配他们真正应该的位置(想想<entity name="&don't-match-me;"/>
),但至少它是......
当然,与多次调用replaceAll()
相比,有更有效的方法可以达到相同的效果。
答案 2 :(得分:0)
您可以在文件的befinning处添加实体。查看here了解更多信息。
您还可以查看this thread,其中有人似乎已经实现了EntityResolver接口(您也可以实现EntityResolver2!),您可以在其中动态处理实体(例如,使用您建议的地图)。 / p> 在jdk6中
WARNING: there is a bug!,但您可以使用jdk5
进行尝试