1. 概述

XML(可扩展标记语言)是跨多个平台和应用广泛使用的数据存储和传输格式。然而,尽管它功能强大,XML 仍存在处理文档中无效字符的问题。本文将探讨不同类型的无效字符以及在 XML 处理中如何处理它们。

2. XML 中的有效字符

XML 规范定义了元素内容和属性值中允许的字符。根据 XML 1.0 规范,以下是一些接受的字符范围和示例:

描述

范围

示例

制表符(水平制表符)

9 (TAB)

\t

换行符(新行)

10 (LF)

\n

回车符(返回到行首)

13 (CR)

\r

基本多语言平面(BMP)中的字符,排除代理块

32 至 55295

A, b, &, 1, α (希腊字母 alpha)

补充私用区-A(SMP)中的字符,排除代理块

57344 至 65533

😊 (微笑),🎉 (派对气球)

BMP 之外的补充平面中的字符

65536 至 1114111

🌍 (地球仪),🚀 (火箭)

注意:在 Unicode 中,我们使用特定的 UTF-16 编码代码点范围来表示超出基本多语言平面的字符。

3. XML 1.1 及其对无效字符的处理

XML 1.1 是 XML 1.0 的更新版本,提供了额外的灵活性,支持更广泛的字符集,包括整个 Unicode 字符集。它允许 1-31 范围内的字符(除制表符、换行符和回车符外),还包括某些控制字符,如 NEL(下一个行,Unicode 0x0085)。

4. XML 中的无效字符

XML 中的无效字符通常分为两类:

4.1. 保留字符

XML 在其语法中保留了一些字符,如 <, >, &, ", 和 '。如果这些字符在没有正确编码的情况下出现在 XML 元素中,可能会干扰解析过程,使 XML 文档无效。例如:

@Test
void givenXml_whenReservedCharacters_thenThrowException() {
    String invalidXmlString = "<?xml version=\"1.1\" encoding=\"UTF-8\"?><root><name>John & Doe</name></root>";
    assertThrowsExactly(SAXParseException.class, () -> parseXmlString(invalidXmlString));
}

应使用预定义的字符实体正确转义保留字符。例如:

  • < 应编码为 &lt;
  • > 应编码为 &gt;
  • & 应编码为 &amp;
  • " 应编码为 &quot;
  • ' 应编码为 &apos;

我们可以执行如下测试来验证:

@Test
void givenXml_whenReservedCharactersEscaped_thenSuccess() {
    String validXmlString = "<?xml version=\"1.1\" encoding=\"UTF-8\"?><root><name>John &amp; Doe</name></root>";

    assertDoesNotThrow(() -> {
        Document document = parseXmlString(validXmlString);

        assertNotNull(document);
        assertEquals("John & Doe", document.getElementsByTagName("name").item(0).getTextContent());
    });
}

另一种处理保留字符的方法是使用 CDATA 段。它可以包含通常被解释为标记的文本块:

@Test
void givenXml_whenUsingCdataForReservedCharacters_thenSuccess() {
    String validXmlString = "<?xml version=\"1.1\" encoding=\"UTF-8\"?><root><name><![CDATA[John & Doe]]></name></root>";

    assertDoesNotThrow(() -> {
        Document document = parseXmlString(validXmlString);

        assertNotNull(document);
        assertEquals("John & Doe", document.getElementsByTagName("name").item(0).getTextContent());
    });
}

4.2. Unicode 字符

XML 文档使用 Unicode 进行编码,支持来自不同语言和脚本的大量字符。虽然 Unicode 提供了广泛的支持,但也包含与 XML 编码标准不兼容的字符,导致解析错误。

让我们看一个测试场景,其中我们在 XML 中包含了记录分隔符。Unicode 将记录分隔符表示为 \u001E

@Test
void givenXml_whenUnicodeCharacters_thenThrowException() {
    String invalidXmlString = "<?xml version=\"1.1\" encoding=\"UTF-8\"?><root><name>John \u001E Doe</name></root>";
    assertThrowsExactly(SAXParseException.class, () -> parseXmlString(invalidXmlString));
}

这个字符的 ASCII 值为 30,超出了接受范围。因此,解析此字符的测试会失败。为了正确处理非 ASCII 字符,我们应该使用如 UTF-8 或 UTF-16 等 Unicode 方案进行编码。

这样可以确保跨平台兼容性,并避免数据损坏问题。现在,让我们执行一个带有适当编码的测试:

@Test
void givenXml_whenUnicodeCharactersEscaped_thenSuccess() {
    String validXmlString = "<?xml version=\"1.1\" encoding=\"UTF-8\"?><root><name>John &#x1E; Doe</name></root>";
    assertDoesNotThrow(() -> {
        Document document = parseXmlString(validXmlString);

        assertNotNull(document);
        assertEquals("John \u001E Doe", document.getElementsByTagName("name").item(0).getTextContent());
    });
}

5. 总结

本文探讨了 XML 中的不同无效字符及其处理方法。通过理解无效字符的原因并采用适当的策略,开发人员可以确保他们的 XML 处理管道的健壮性和可靠性。

如往常一样,完整的源代码可在 GitHub 上找到。