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

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

提问人:jvhashe 提问时间:6/27/2017 最后编辑:kjhughesjvhashe 更新时间:10/12/2022 访问量:35018

问:

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

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

正如你所知道的,描述里面有一个似乎是无效的标签()。现在,这个描述标签已知是一个叶子标签,里面不应该有任何嵌套标签。无论如何,这仍然是一个问题,并产生了一个例外<THIS-IS-PART-OF-DESCRIPTION>DocumentBuilder.parse(...)

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

Java XML XML 解析 xml-validation

评论

2赞 Makoto 6/27/2017
但是,无效的 XML 实际上不是 XML。存在期望 XML 有效的解析器,期望 XML 是有效的,期望这样做也不是一个飞跃;它不像 DOM 那样可以完全无效。
2赞 Compass 6/27/2017
从设计的角度来看,纠正格式错误的 XML 应该是提供者的责任,而不是处理格式错误的 XML 的使用者的责任。
1赞 Lew Bloch 6/27/2017
无法测试 XML 的有效性,因为它的格式不正确。“有效”意味着文档符合架构或 DTD,但如果文档甚至不是格式正确的 XML,那么甚至不能提出有效性问题。你的代码要做的正确事情是拒绝错误的输入。默默地忽略这些严重的错误是导致更糟糕错误的秘诀。
0赞 vtd-xml-author 6/27/2017
您可以使用 shell 脚本或解释型语言(如 Perl)来修补错误以使其有效。

答:

4赞 Jim Garrison 6/27/2017 #1

根据设计,标准 XML 解析器永远不会接受无效的 XML。

唯一的选择是预处理输入以删除“可预见的无效”内容,或在解析之前将其包装在 CDATA 中。

评论

1赞 Lew Bloch 6/27/2017
“标准 XML 解析器永远不会接受无效的 XML”,也不会接受格式不正确的假定 XML。
41赞 kjhughes 6/27/2017 #2

“XML”比无效更糟糕——它的格式不正确;请参阅格式正确的 XML 与有效的 XML

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

选项,最理想的第一:

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

  2. 在解析为 XML 之前,使用宽容标记解析器清理问题:

  3. 使用文本编辑器手动将数据处理为文本,或者 以编程方式使用字符/字符串函数。这样做 编程范围可以从棘手到不可能,因为 似乎是什么 可预测往往不是——违反规则很少受到规则的约束

    • 对于无效字符错误,请使用正则表达式删除/替换无效字符

      • PHP的: preg_replace('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $s);
      • 红宝石: 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, '')
    • 对于 & 符号,请使用正则表达式将匹配项替换为: credit: blhsindemo&amp;

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

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

评论

0赞 jvhashe 6/27/2017
是的,我认为这些可能是唯一的选择,但我想先看看是否有其他解决方案。感谢您的输入。
0赞 lab9 6/26/2020
处理 & 号的正则表达式:以下正则表达式也将正确处理注释CDATA 部分/&(?!(?:#\d+|#x[0-9a-fA-F]+|\w+);)(?!(?:(?!<!\[CDATA\[|\]\]>).)*\]\]>)(?!(?:(?!<!--|-->).)*-->)/g
0赞 urznow 1/26/2023
xmllint --recover --html --dropdtd --xmlout --format 2>/dev/null file(在最新版本中)做什么。与 xsltprocxmlstarlet 一样,xmllint 依赖于 libxml2,并且通常与 libxml2 一起分发。注意:在版本 20913 中,列出的选项比 更多。xmlstarlet format -R -H -D 2>/dev/null filexmllint --helpman xmllint
1赞 Benj 5/29/2018 #3

IMO 这些情况应该使用 JSoup 来解决。

下面是这个特定案例的不是真正的答案,但在网上找到了这个(感谢 Coderwall 上的 inuyasha82)。在处理格式错误的 XML 时,这个代码位确实启发了我另一个类似的问题,所以我在这里分享它。

请不要编辑下面的内容,因为它与原始网站上的内容相同。

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

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

但是,如果您有以下文档:

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

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

在此示例中,有一个关于如何解决该问题并成功解析上述格式错误的 xml 的解决方案。

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

因此,首先,您必须打开包含“格式错误”的 xml(即文件)的资源:

File file = new File(pathtofile);

然后打开一个 FileInputStream:

FileInputStream fis = new FileInputStream(file);

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

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

  1. 一个 ByteIputStream 元素,其中包含以下字符串:<root>
  2. 我们的 FileInputStream
  3. 带有以下字符串的 ByteInputStream:</root>

所以代码是:

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

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

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

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

3赞 imhotap 5/31/2018 #4

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

我想补充一点,这个以及许多其他格式不正确和/或DTD无效的XML情况可以使用SGML(HTML和XML的ISO标准化超集)进行修复。在你的例子中,有效的方法是将虚假元素声明为SGML空元素,然后使用例如。程序(OpenSP/OpenJade SGML 包的一部分)将其转换为 XML。例如,如果您向THIS-IS-PART-OF-DESCRIPTIONosxosx

<!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 工具进行进一步处理。

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