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

时间:2017-06-26 17:22:23

标签: java xml xml-parsing xml-validation

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

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

正如您所知,描述中的内容似乎是无效标记(<THIS-IS-PART-OF-DESCRIPTION>)。现在,已知此描述标记是叶标记,并且不应在其中包含任何嵌套标记。无论如何,这仍然是一个问题,并在DocumentBuilder.parse(...)

上产生例外

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

4 个答案:

答案 0 :(得分:18)

“XML”比无效更糟糕 - 它的形式不正确;请参阅 Well Formed vs Valid 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);
      • 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, '')
    • 对于&符号,请使用正则表达式将匹配项替换为&amp; credit:blhsindemo

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

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

IMO应使用JSoup解决这些案例。

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

请不要编辑下面的内容,就像在原始网站上一样。

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

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

但是如果你有一个类似的文件:

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

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

在此示例中,有一个解决方案可以解决该问题,并成功解析上面的格式错误的xml。

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

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

File file = new File(pathtofile);

然后打开FileInputStream:

FileInputStream fis = new FileInputStream(file);

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

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

包含字符串的ByteIputStream元素:“” 我们的FileInputStream 带字符串的ByteInputStream:“” 所以代码是:

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 Parser库,它将被解析而没有任何问题。 (检查Stax库);

答案 3 :(得分:1)

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

我想补充一点,使用SGML(ISO标准化的HTML超集)可以修复many other未正确和/或DTD无效的XML案例和XML。在您的情况下,有效的是将伪造的THIS-IS-PART-OF-DESCRIPTION元素声明为SGML空元素,然后使用eg。 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等开头的元素名称以XML格式保留,并赢得了#39 ;是否符合XML解析器。