【问题标题】:How to make XML Parser aware of all Character Entity References?如何让 XML Parser 知道所有字符实体引用?
【发布时间】:2016-12-10 19:06:33
【问题描述】:

我从服务器获取任意 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("&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?

【问题讨论】:

标签: java xml parsing xml-parsing


【解决方案1】:

如果您可以更改代码以使用 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 的选项,但我找不到任何从外部设置它的方法。

【讨论】:

  • 真正可悲的是,扫描实体引用并抛出异常 (com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEntityReference(XMLStringBuffer)) 的代码实际上检查了 fReplaceEntityReferences 选项标志。如果我在调试器中手动将其调整为false,则代码会按照您的需要构建 DOM。但似乎无法从公共 API 设置它,也无法访问实现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-11
  • 1970-01-01
  • 2011-12-20
  • 1970-01-01
  • 1970-01-01
  • 2021-02-13
  • 2011-01-26
相关资源
最近更新 更多