如何处理未知的实体引用?

时间:2010-03-12 16:19:44

标签: java xml parsing

我正在解析(很多)包含实体引用的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.

有什么建议吗?当然,我已经很乐意用空字符串替换未知实体。

谢谢,

3 个答案:

答案 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

进行尝试