【问题标题】:Filtering illegal XML characters in Java过滤 Java 中的非法 XML 字符
【发布时间】:2011-02-23 05:39:01
【问题描述】:

XML 规范定义了 XML 文档中允许的 Unicode 字符子集: http://www.w3.org/TR/REC-xml/#charsets.

如何从 Java 中的字符串中过滤掉这些字符?

简单的测试用例:

  Assert.equals("", filterIllegalXML(""+Character.valueOf((char) 2)))

【问题讨论】:

  • 为什么你会得到这些“非法”的 XML 字符?一旦检测到它们,您想对它们做什么?删除?替换?
  • @RH:忽略它们就足够了。最好的解决方案是删除它们并获得某种报告。这样我可以记录警告。
  • 如果有人想知道我利用了来自 Xerces 的 XMLChar,正如 ZZ Coder 所建议的那样。你可以在这里找到整个方法:pastebin.com/6Vbm1zuC

标签: java xml unicode


【解决方案1】:

找出 XML 的所有无效字符并非易事。您需要从 Xerces 调用或重新实现 XMLChar.isInvalid(),

http://kickjava.com/src/org/apache/xerces/util/XMLChar.java.htm

【讨论】:

  • 这个类非常复杂[阅读:很难理解——无论如何对我来说,这要归功于它的机器生成部分],并且需要实例化和预传播一个 64K CHARS 数组......
【解决方案2】:

This page 包含一个 Java 方法,用于通过测试每个字符是否符合规范来去除 invalid XML characters,但它不检查 highly discouraged 字符

顺便说一句,转义字符不是解决方案,因为 XML 1.0 和 1.1 规范也不允许转义形式的无效字符。

【讨论】:

【解决方案3】:

这是一个处理原始字符以及流中转义字符的解决方案,适用于 stax 或 sax。它需要扩展其他无效字符,但你明白了

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;

import org.apache.commons.io.IOUtils;
import org.apache.xerces.util.XMLChar;

public class IgnoreIllegalCharactersXmlReader extends Reader {

    private final BufferedReader underlyingReader;
    private StringBuilder buffer = new StringBuilder(4096);
    private boolean eos = false;

    public IgnoreIllegalCharactersXmlReader(final InputStream is) throws UnsupportedEncodingException {
        underlyingReader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
    }

    private void fillBuffer() throws IOException {
        final String line = underlyingReader.readLine();
        if (line == null) {
            eos = true;
            return;
        }
        buffer.append(line);
        buffer.append('\n');
    }

    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        if(buffer.length() == 0 && eos) {
            return -1;
        }
        int satisfied = 0;
        int currentOffset = off;
        while (false == eos && buffer.length() < len) {
            fillBuffer();
        }
        while (satisfied < len && buffer.length() > 0) {
            char ch = buffer.charAt(0);
            final char nextCh = buffer.length() > 1 ? buffer.charAt(1) : '\0';
            if (ch == '&' && nextCh == '#') {
    final StringBuilder entity = new StringBuilder();
    // Since we're reading lines it's safe to assume entity is all
    // on one line so next char will/could be the hex char
    int index = 0;
    char entityCh = '\0';
    // Read whole entity
    while (entityCh != ';') {
        entityCh = buffer.charAt(index++);
        entity.append(entityCh);
    }
    // if it's bad get rid of it and clean it from the buffer and point to next valid char
    if (entity.toString().equals("&#2;")) {
        buffer.delete(0, entity.length());
        continue;
    }
            }
            if (XMLChar.isValid(ch)) {
    satisfied++;
    cbuf[currentOffset++] = ch;
            }
            buffer.deleteCharAt(0);
        }
        return satisfied;
    }

    @Override
    public void close() throws IOException {
        underlyingReader.close();
    }

    public static void main(final String[] args) {
        final File file = new File(
    <XML>);
        final File outFile = new File(file.getParentFile(), file.getName()
    .replace(".xml", ".cleaned.xml"));
        Reader r = null;
        Writer w = null;
        try {
            r = new IgnoreIllegalCharactersXmlReader(new FileInputStream(file));
            w = new OutputStreamWriter(new FileOutputStream(outFile),"UTF-8");
            IOUtils.copyLarge(r, w);
            w.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(r);
            IOUtils.closeQuietly(w);
        }
    }
}

【讨论】:

    【解决方案4】:

    大致基于 Stephen C 的答案链接中的 comment 和 XML 1.1 的维基百科 spec 这是一个 java 方法,向您展示如何使用正则表达式替换删除非法字符:

    boolean isAllValidXmlChars(String s) {
      // xml 1.1 spec http://en.wikipedia.org/wiki/Valid_characters_in_XML
      if (!s.matches("[\\u0001-\\uD7FF\\uE000-\uFFFD\\x{10000}-\\x{10FFFF}]")) {
        // not in valid ranges
        return false;
      }
      if (s.matches("[\\u0001-\\u0008\\u000b-\\u000c\\u000E-\\u001F\\u007F-\\u0084\\u0086-\\u009F]")) {
        // a control character
        return false;
      }
    
      // "Characters allowed but discouraged"
      if (s.matches(
        "[\\uFDD0-\\uFDEF\\x{1FFFE}-\\x{1FFFF}\\x{2FFFE}–\\x{2FFFF}\\x{3FFFE}–\\x{3FFFF}\\x{4FFFE}–\\x{4FFFF}\\x{5FFFE}-\\x{5FFFF}\\x{6FFFE}-\\x{6FFFF}\\x{7FFFE}-\\x{7FFFF}\\x{8FFFE}-\\x{8FFFF}\\x{9FFFE}-\\x{9FFFF}\\x{AFFFE}-\\x{AFFFF}\\x{BFFFE}-\\x{BFFFF}\\x{CFFFE}-\\x{CFFFF}\\x{DFFFE}-\\x{DFFFF}\\x{EFFFE}-\\x{EFFFF}\\x{FFFFE}-\\x{FFFFF}\\x{10FFFE}-\\x{10FFFF}]"
      )) {
        return false;
      }
    
      return true;
    }
    

    【讨论】:

      【解决方案5】:

      使用commons-lang 中的StringEscapeUtils.escapeXml(xml) 将转义,而不是过滤字符。

      【讨论】:

      • 我已经在使用这种方法来转义实体(例如 &amp;lt;&amp;lt;),但这是不同的。该方法似乎没有过滤任何非法字符。我的“测试用例”失败了。
      • 如问题所述:assertEquals("", StringEscapeUtils.escapeXml(""+Character.valueOf((char) 2)));
      • 啊,对不起。好吧,我不确定这个角色有没有办法进入 xml :) 也许 commons-lang 错过了它。实际上 - 你的 commons-lang 版本是什么?
      • 我的项目目前使用的是 2.4,但我也刚刚在 2.5 中检查过。没有区别。
      • 来自文档:StringEscapeUtils.escapeXml(xml) 仅支持五个基本 XML 实体(gt、lt、quot、amp、apos)。不支持 DTD 或外部实体。
      【解决方案6】:

      使用escapeXml10escapeXml11。这些函数转义了"&amp;'&lt;&gt; 等字符,还可以过滤无效字符。

      对于那些不想过滤无效字符但使用不同的转义系统转义它们的人,请在此处查看我的答案https://stackoverflow.com/a/59475093/3882565

      【讨论】:

        【解决方案7】:

        您可以使用regex (Regular Expression) 来完成这项工作,请参阅 cmets here 中的示例

        【讨论】:

          猜你喜欢
          • 2011-06-12
          • 1970-01-01
          • 1970-01-01
          • 2010-12-15
          • 2019-02-25
          • 2011-03-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多