java - 如何让 XML 解析器知道所有字符实体引用?

标签 java xml parsing xml-parsing

我从服务器获取任意 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 "被引用,但未声明。

我可以通过预处理原始 xmlStr 并在解析之前翻译所有有问题的实体引用来解决这个问题。这是一个有效的虚拟实现:

protected static String translateEntityReferences(String xml) {
    String newXml = xml;
    Map<String, String> entityRefs = new HashMap<>();
    entityRefs.put("&nbsp;", "&#160;");
    entityRefs.put("&laquo;", "&#171;");
    entityRefs.put("&raquo;", "&#187;");
    // ... 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?

最佳答案

如果您可以更改代码以使用 StAX 而不是 DOM,简单的解决方案是使用 XMLInputFactory 属性 IS_REPLACING_ENTITY_REFERENCES 设置为 false

public static void main(String[] args) throws Exception
{
    String doc = "<doc>&nbsp;</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();
    }
}

输出:

Start Document
Start Element: doc
Entity: nbsp null
End Element: doc

但是如果您真的需要内存中的完整 DOM 树,那么这可能需要在您的代码中进行过多的重写。

我花了一个小时跟踪 DOM 实现,但找不到任何方法让 DOM 解析器从 XMLStreamReader 读取数据。

代码中也有证据表明内部 DOM 解析器实现有一个类似于 IS_REPLACING_ENTITY_REFERENCES 的选项,但我找不到从外部设置它的任何方法。

关于java - 如何让 XML 解析器知道所有字符实体引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38771056/

相关文章:

java - 为什么当我在 64 位 jvm 中打开带有小程序的页面时 IE 11 会崩溃,但在 32 位 jvm 中却可以正常工作?

java - 从 Java 代码运行报告时,Jasper Reports 不格式化数字

具有多重继承的 XML 模式

删除 XML 中的属性的 SQL 查询

postgresql - 从字符串中解析间隔的最佳方法是什么?

python - 正则表达式忽略特定字符

java - Vertx HttpCllentRequest 重定向

java - 如何通过 SSL 连接配置 TeamCity 电子邮件通知?

java - 将复选框放入 ScrollView 后访问复选框时出错

parsing - Jsoup 与 GWT(制作兼容的 JAR)