如何解析无效(错误/格式不正确)XML?

How to parse invalid (bad / not well-formed) XML?

目前,我正在研究一项涉及解析我们从其他产品接收到的 XML 的功能。我决定 运行 针对一些实际客户数据进行一些测试,看起来其他产品允许用户输入应该被视为无效的输入。无论如何,我仍然必须尝试找出一种解析它的方法。我们正在使用 javax.xml.parsers.DocumentBuilder,我在输入时遇到如下所示的错误。

<xml>
  ...
  <description>Example:Description:<THIS-IS-PART-OF-DESCRIPTION></description>
  ...
</xml>

如您所知,说明中包含似乎无效的标签 (<THIS-IS-PART-OF-DESCRIPTION>)。现在,这个描述标签被认为是一个叶子标签,里面不应该有任何嵌套标签。无论如何,这仍然是一个问题并在 DocumentBuilder.parse(...)

上产生异常

我知道这是无效的 XML,但可以预见它是无效的。关于解析此类输入的方法有什么想法吗?

标准 XML 解析器将永远不会接受无效的 XML,这是设计使然。

您唯一的选择是预处理输入以删除 "predictably invalid" 内容,或者在解析之前将其包装在 CDATA 中。

那个“XML”比无效更糟糕——它格式不正确;见 Well Formed vs Valid XML.

对违法行为的可预测性进行非正式评估无济于事。该文本数据不是 XML。没有符合标准的 XML 工具或库可以帮助您处理它。

选项,最理想的优先:

  1. 请提供商自行解决问题。 要求格式正确 XML.(从技术上讲,短语 格式正确 XML 是多余的,但可能有助于强调.)

  2. 使用 容错标记解析器 在解析为 XML:

    之前清除问题
  3. 使用文本编辑器手动将数据处理为文本 或 以编程方式使用 character/string 函数。这样做 以编程方式可以从 棘手到不可能 为 看起来是什么 可预测的往往不是 -- 打破规则很少受到规则的约束

    • 对于无效字符错误,使用正则表达式来remove/replace无效字符:

      • PHP: preg_replace('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $s);
      • Ruby: string.tr("^\u{0009}\u{000a}\u{000d}\u{0020}-\u{D7FF}\u{E000‌​}-\u{FFFD}", ' ')
      • JavaScript: inputStr.replace(/[^\x09\x0A\x0D\x20-\xFF\x85\xA0-\uD7FF\uE000-\uFDCF\uFDE0-\uFFFD]/gm, '')
    • 对于 ampersands,使用正则表达式将匹配项替换为 &amp;: credit: , demo

      &(?!(?:#\d+|#x[0-9a-f]+|\w+);)
      

请注意,上面的正则表达式不会接受注释或 CDATA 部分考虑在内。

IMO 这些情况应该使用 JSoup.

来解决

下面是针对此特定案例的非真实答案,但找到了 this on the web(感谢 Coderwall 上的 inuyasha82)。在处理格式错误的 XMLs 时,这段代码确实启发了我另一个类似的问题,所以我在这里分享它。

下面的内容请不要编辑,因为它是在原网站上的。

XML格式,要求文档中声明的唯一根元素是有效的。 例如,一个有效的 xml 是:

<root>
     <element>...</element>
     <element>...</element>
</root>

但是如果你有这样的文档:

<element>...</element>
<element>...</element>
<element>...</element>
<element>...</element>

这将被视为格式错误的 XML,因此许多 xml 解析器只是抛出一个异常,抱怨没有根元素。等等

在这个例子中有一个关于如何解决这个问题并成功解析上面格式错误的 xml 的解决方案。

基本上我们要做的是以编程方式添加根元素。

所以首先你必须打开包含你的 "malformed" xml 的资源(即一个文件):

File file = new File(pathtofile);

然后打开一个FileInputStream:

FileInputStream fis = new FileInputStream(file);

如果我们此时尝试使用任何 XML 库解析此流,我们将引发格式错误的文档异常。

现在我们创建一个包含三个元素的 InputStream 对象列表:

包含以下字符串的 ByteIputStream 元素:“” 我们的文件输入流 带有字符串的 ByteInputStream:“” 所以代码是:

List<InputStream> streams = 
    Arrays.asList(
        new ByteArrayInputStream("<root>".getBytes()),
    fis,
    new ByteArrayInputStream("</root>".getBytes()));

现在使用 SequenceInputStream,我们为上面创建的列表创建一个容器:

InputStream cntr = 
new SequenceInputStream(Collections.enumeration(str));

现在我们可以在 cntr 上使用任何 XML 解析器库,它将被毫无问题地解析。 (与 Stax 库核实);

接受的答案是很好的建议,并且包含非常有用的链接。

我想补充一点,many 格式不正确的情况 and/or DTD 无效 XML 可以使用 SGML 修复,SGML 是 ISO 标准化的超集HTML 和 XML。在您的情况下,有效的方法是将伪造的 THIS-IS-PART-OF-DESCRIPTION 元素声明为 SGML 空元素,然后使用例如。 osx 程序(OpenSP/OpenJade SGML 包的一部分)将其转换为 XML。例如,如果您向 osx

提供以下内容
<!DOCTYPE xml [
  <!ELEMENT xml - - ANY>
  <!ELEMENT description - - ANY>
  <!ELEMENT THIS-IS-PART-OF-DESCRIPTION -  - EMPTY>
]>
<xml>
  <description>blah blah
    <THIS-IS-PART-OF-DESCRIPTION>
  </description>
</xml>

它将输出格式正确的 XML 以便使用您选择的 XML 工具进一步处理。

但是请注意,您的示例代码段还有另一个问题,即以字母 xmlXMLXml 等开头的元素名称在 [=29= 中保留],并且不会被符合 XML 的解析器接受。