在Java中去除无效的XML字符

29
我有一个XML文件是从数据库输出的。我正在使用Java SAX解析器来解析XML并以不同的格式输出它。该XML包含一些无效字符,解析器会抛出错误,例如“Invalid Unicode character (0x5)”。
除了逐行预处理文件并替换这些字符外,是否有好的方法可以剥离所有这些字符?到目前为止,我遇到了三种不同的无效字符(0x5、0x6和0x7)。该数据库转储约有4GB大小,我们将对其进行多次处理,因此每次获取新的转储并运行预处理程序需要额外等待30分钟,这将很麻烦,而且我已经不是第一次遇到这个问题。

如果文件包含无效字符,则它不是XML文件。请要求创建者在未来只创建格式良好的XML文件。我过去经常遇到这个问题。人们似乎不理解XML需要是格式良好的,而且不能包含垃圾。 - MarkR
我完全同意。不幸的是,这并非总是可能的(无能的技术人员、合同措辞等)。 - Mason
2
这些字符有任何意义吗?它们可能不是随机损坏,因此去除它们会丢失信息吗? - Bart Schuller
6个回答

23

我使用了Xalan的org.apache.xml.utils.XMLChar类:

public static String stripInvalidXmlCharacters(String input) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < input.length(); i++) {
        char c = input.charAt(i);
        if (XMLChar.isValid(c)) {
            sb.append(c);
        }
    }

    return sb.toString();
}

我认为这个方法对代理字符无效:XMLChar#isValid()会分别针对高位和低位返回false,但如果将它们组合起来是有效的,则会返回true。 - ankon

10

我个人没有使用过这个工具,但Atlassian开发了一个命令行XML清理器,可能适合您的需要(主要是为JIRA开发的,但XML就是XML):

下载atlassian-xml-cleaner-0.1.jar

在DOS控制台或shell中定位计算机上的XML或ZIP备份文件,假定文件名为data.xml

运行: java -jar atlassian-xml-cleaner-0.1.jar data.xml > data-clean.xml

这将把data.xml的副本写入data-clean.xml,删除无效字符。


有其他人链接也打不开吗? - But I'm Not A Wrapper Class
如果要为市场构建插件,则可在com.atlassian.core.util.xml.XMLCleaningReader上使用替换无效字符的相同类。 - Vitor Pelizza
来自未来的信息(2020年)-第二个链接对我有效,这个JAR文件解决了我遇到的一个严重问题,即成千上万个包含各种非法字符的XML文件。通过这个工具运行它们可以使它们标准化,并且可以被Python的lxml库解析。未来感谢你。 - lonstar

8
我使用以下正则表达式,它似乎在JDK6中按预期工作:
Pattern INVALID_XML_CHARS = Pattern.compile("[^\\u0009\\u000A\\u000D\\u0020-\\uD7FF\\uE000-\\uFFFD\uD800\uDC00-\uDBFF\uDFFF]");
...
INVALID_XML_CHARS.matcher(stringToCleanup).replaceAll("");

在JDK7中,可以使用符号 \x{10000}-\x{10FFFF} 来表示BMP之外的最后一个范围,而不是那个难以理解的符号\uD800\uDC00-\uDBFF\uDFFF

3

当我将澳大利亚出口关税的内容解析成XML文档时,我遇到了类似的问题。我不能使用这里提出的解决方案,例如: - 使用从命令行调用的外部工具(一个jar)。 - 要求澳大利亚海关清理源文件。

目前唯一解决此问题的方法是逐个字符地迭代整个源文件的内容,并测试每个字符是否不属于ASCII范围0x00至0x1F(包括0x1F)。虽然可以做到,但我想知道是否有更好的方法使用Java字符串类型的方法。

编辑 我找到了一个解决方案,可能对他人有用:使用Java方法String#ReplaceAll替换或删除XML文档中的任何不良字符。

示例代码(我删除了一些必要的语句以避免混乱):

BufferedReader reader = null;
...
String line = reader.readLine().replaceAll("[\\x00-\\x1F]", "");

在这个例子中,我删除(即用一个空字符串替换)0x00到0x1F范围内的不可打印字符。您可以更改#replaceAll()方法中的第二个参数,以使用您的应用程序所需的字符串替换字符。


0

你的无效字符是否仅存在于值中而不是标签本身,即XML在概念上符合模式但值未经适当清理?如果是这样,那么考虑覆盖InputStream以创建一个CleansingInputStream,用其XML等效项替换无效字符。


0

你的问题与XML无关,而是与字符编码有关。归根结底,每个字符串,无论是XML还是其他,都由字节组成,除非告诉你该字符串具有什么字符编码,否则你无法知道这些字节表示哪些字符。例如,如果供应商告诉你它是UTF-8,但实际上是其他编码,那么你就会遇到问题。在最好的情况下,一切正常,但某些字节被转换为“错误”的字符。在最坏的情况下,你会遇到像你遇到的那样的错误。

实际上,你的问题甚至更糟:你的字符串包含不代表任何字符编码中的字符的字节序列。没有文本处理工具,更不用说XML解析器可以帮助你解决这个问题。这需要在字节级别进行清理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接