java.lang.IllegalArgumentException: 非法字符 (d83d)

3
当我在我的Android应用程序中保存XML数据文件时,如果用户输入了表情符号,我会遇到异常。
d83d字符是这个:http://apps.timwhitlock.info/unicode/inspect?s=%F0%9F%98%84 - 笑脸字符。
相关的堆栈跟踪:
java.lang.IllegalArgumentException: Illegal character (d83d)
at org.kxml2.io.KXmlSerializer.reportInvalidCharacter(KXmlSerializer.java:144)
at org.kxml2.io.KXmlSerializer.writeEscaped(KXmlSerializer.java:130)
at org.kxml2.io.KXmlSerializer.attribute(KXmlSerializer.java:465)

总体问题是: 如何修复它,以便我的用户不会因表情符号而导致应用程序崩溃...

后续问题包括:

这个KXmlSerializer是否支持表情符号/Unicode?

是否有更新的版本?到目前为止我还没有找到。 这个库是否在积极维护中?

谁在维护KXmlSerializer? 是http://kxml.org/吗?我在那里找不到太多信息...

如果将更新的版本放在类路径上,它不会与Android内置的版本冲突吗?

我应该使用哪个其他xml写入库来替换KXmlSerializer?

编辑:我应该补充说明,应用程序通过xml保存的文本来自标准的Android EditText UI小部件,用户在其中输入文本(我没有对字符串进行任何Unicode级别的操作)。


这个补丁可能是相关的:https://android.googlesource.com/platform/libcore/+/3f1a5eb%5E!/ 但我想知道在哪里获取正确的二进制文件。 - KarolDepka
可能与Android联系人同步表情符号有关的问题:https://code.google.com/p/android/issues/detail?id=64108。一个项目成员写道:“我在L中修复了KXmlSerializer。” - KarolDepka
你知道如何解决这个问题吗?我也想知道在哪里找到最新的依赖项,以修复它。对我来说,它仍然显示为2.3.0,这是在2009年最后更新的版本。 - Shivam Pokhriyal
2个回答

3
我在我的测试设备(API17)上遇到了相同的异常。这是代码:
final XmlSerializer serializer = new Xml.newSerializer();
final StringWriter stringWriter = new StringWriter();
serializer.setOutput(stringWriter);
serializer.startDocument("UTF-8", true);
serializer.startTag("", "XXXX");
....
serializer.endTag("", "XXXX");
serializer.endDocument();
writer.write(stringWriter.toString());
writer.close();

但是该代码在API21和API23上运行正常。

在API17中,KXmlSerializer的关键代码:

// BEGIN android-changed: refuse to output invalid characters
// See http://www.w3.org/TR/REC-xml/#charsets for definition.
// No other Java XML writer we know of does this, but no Java
// XML reader we know of is able to parse the bad output we'd
// otherwise generate.
// Note: tab, newline, and carriage return have already been
// handled above.
boolean valid = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
if (!valid) {
    reportInvalidCharacter(c);
}
if (unicode || c < 127) {
    writer.write(c);
} else {
    writer.write("&#" + ((int) c) + ";");
}
// END android-changed

在API23中,KXmlSerializer的键代码:
// BEGIN android-changed: refuse to output invalid characters
// See http://www.w3.org/TR/REC-xml/#charsets for definition.
// No other Java XML writer we know of does this, but no Java
// XML reader we know of is able to parse the bad output we'd
// otherwise generate.
// Note: tab, newline, and carriage return have already been
// handled above.
boolean allowedInXml = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
if (allowedInXml) {
    if (unicode || c < 127) {
        writer.write(c);
    } else {
        writer.write("&#" + ((int) c) + ";");
    }
} else if (Character.isHighSurrogate(c) && i < s.length() - 1) {
    writeSurrogate(c, s.charAt(i + 1));
    ++i;
} else {
    reportInvalidCharacter(c);
}
// END android-changed

异常的原因,你可以阅读Stephen C.的答案。

最后,我从kXML(https://sourceforge.net/projects/kxml/files/kxml2/2.3.0/)下载了类KXmlSerializer的最新代码,并将其添加到我的项目中,将该类重命名为TestKXmlSerializer。使用该类的方式如下:

final XmlSerializer serializer = new TestKXmlSerializer();

可在API17、API20和API23中使用。


2
这里是我认为引起问题的代码。
boolean allowedInXml = (c >= 0x20 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd);
if (allowedInXml) {
    if (unicode || c < 127) {
        append(c);
    } else {
        append("&#" + ((int) c) + ";");
    }
} else if (Character.isHighSurrogate(c) && i < s.length() - 1) {
    writeSurrogate(c, s.charAt(i + 1));
    ++i;
} else {
    reportInvalidCharacter(c);
}

(在此处找到:https://android.googlesource.com/platform/libcore/+/master/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
看起来您正在尝试编写 Unicode 代码点 1F604,该代码点表示为代理对 D83D DE04。请注意,“高”代理范围为 D800-DBFF。
如果我们将其输入代码中,则会发现 D83D 应该是可接受的,但前提是它后面跟着另一个字符。(writeSurrogate 方法检查第二个字符是否为低代理,但如果不是,则会得到不同的异常消息。)
因此,我的诊断是某种方式导致您丢失了构成表情符号的第二个字符。在指责序列化程序类之前,请检查该属性的值以确认/反驳该诊断。它尝试执行的检查完全合法。
更新 有迹象表明您可能正在使用早期版本的序列化程序,该版本不支持序列化非 BMP 代码点。 (如果是这种情况,则我的诊断将是不正确的。)
我不知道您将如何解决这个问题。

嗨。谢谢。编辑:我应该补充一下,该应用程序保存的文本通过标准的Android EditText UI小部件输入文本(我没有对字符串进行任何Unicode级别的操作)。似乎“支持非BMP字符”是在后来添加的(2014年)- https://android.googlesource.com/platform/libcore/+/3f1a5eb%5E!/。如果目标设备有旧版本怎么办?我可以发布更新后的XML类吗? - KarolDepka

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