问题存在。但不是针对16位(2字节)Unicode字符,范围从
0x0000
到
0xFFFF
。问题在于需要超过2个字节的字符在Unicode编码中。这些字符被称为
Java Character中的“Unicode代码点”:“Unicode代码点用于U+0000至U+10FFFF之间的字符值,并且Unicode代码单元用于UTF-16编码的16位char值的代码单元。” Java平台在char数组以及String和StringBuffer类中使用UTF-16表示法。在该表示法中,补充字符(其代码点大于U+FFFF的字符)表示为一对char值,第一个来自高代理范围(\uD800-\uDBFF),第二个来自低代理范围(\uDC00-\uDFFF)。
问题出现在org.apache.xmlbeans.impl.store.Saver
中。它使用了一个private char[] _buf
。但是由于char
的最大值是0xFFFF
,所以无法存储Unicode代码点在0x10000
到0x10FFFF
之间的字符。因此,它们将被存储为一对char值。
有一个方法
/**
* Test if a character is valid in xml character content. See
* http://www.w3.org/TR/REC-xml#NT-Char
*/
private boolean isBadChar ( char ch )
{
return ! (
(ch >= 0x20 && ch <= 0xD7FF ) ||
(ch >= 0xE000 && ch <= 0xFFFD) ||
(ch >= 0x10000 && ch <= 0x10FFFF) ||
(ch == 0x9) || (ch == 0xA) || (ch == 0xD)
);
}
这段代码存在严重的漏洞,因为它检查一个char
是否在0x10000
和0x10FFFF
之间。正如先前提到的那样,这根本不可能。
它也排除了高代理范围(\uD800-\uDBFF)和低代理范围(\uDC00-\uDFFF)作为坏字符。因此,将代码点表示为一对char
值将被排除。
问题源自org.apache.xmlbeans.impl.store.Saver
中的一个错误。
补丁:
目标: 不要将高代理范围(\uD800-\uDBFF)和低代理范围(\uDC00-\uDFFF)作为坏字符排除。因此,在XML
中存储的两个16位chars
表示的Unicode代码点将不会被排除。
下载Saver.java。将private boolean isBadChar ( char ch )
更改为
private boolean isBadChar ( char ch )
{
return ! (
(ch >= 0x20 && ch <= 0xFFFD ) ||
(ch == 0x9) || (ch == 0xA) || (ch == 0xD)
);
}
在
static final class OptimizedForSpeedSaver
和
static final class TextSaver
两个类中都需要这样做。
编译
Saver.java
。
在类路径之外的某个地方存储
xmlbeans-2.6.0.jar
的备份。
将
xmlbeans-2.6.0.jar
中的
Saver$OptimizedForSpeedSaver.class
和
Saver$TextSaver.class
替换为新编译的文件->
/org/apache/xmlbeans/impl/store/
。
现在,Unicode代码点U+10000以上的内容将存储在
sharedStrings.xml
中。
免责声明:
此方法未经过充分测试,不应用于生产环境。仅用于描述问题。或许
xmlbeans.apache.org
上的一些程序员会抽出时间来妥善解决
org.apache.xmlbeans.impl.store.Saver
的问题。
更新:
现在已经有
xmlbeans-2.6.2.jar可用,其中已包含此修补程序。
更新:
现在已经有
xmlbeans-3.0.0.jar可用,其中也已包含此修补程序。
它的作用是:
static boolean isBadChar ( char ch )
{
return ! (
Character.isHighSurrogate(ch) ||
Character.isLowSurrogate(ch) ||
(ch >= 0x20 && ch <= 0xD7FF ) ||
(ch >= 0xE000 && ch <= 0xFFFD) ||
(ch >= 0x10000 && ch <= 0x10FFFF) ||
(ch == 0x9) || (ch == 0xA) || (ch == 0xD)
);
}
所以它检查
char ch
是否是
HighSurrogate
或
LowSurrogate
,如果是,那么它不是坏字符。好的。
但尽管如此,它仍然检查
char ch
是否大于或等于
0x10000
。再次强调:这对于
char
是不可能的!
char
的最大值是
0xFFFF
。
"\u4e03"
(七)- 这样Java源编码就不会产生干扰。 - Joop Eggen"\ud840\udc00"
(2个UTF-16字符)==new String(new int[] { 0x20000 }, 0, 1)
。不过还是谢谢,确实可能存在关于两个字符的错误:单个字符处理等等。(然而我的评论只是为了简单地排除不太可能的错误,即Java编译器使用与编辑器不同的编码方式。) - Joop Eggenorg.apache.xmlbeans.impl.store.Saver
,它不应该将高代理范围(\uD800-\uDBFF)和低代理范围(\uDC00-\uDFFF)排除为坏字符。今天下午会尝试一下。 - Axel Richter