android - XmlPullParser.getInputEncoding() 在 API11+ 和 Android 的 API11 之前版本上的不同行为

标签 android xml encoding filereader fileinputstream

我正在为我的 Android 应用程序开发一项新功能,以启用数据备份和恢复。我正在使用 XML 文件来备份数据。这是一段设置输出文件编码的代码:

XmlSerializer serializer = Xml.newSerializer();
FileWriter fileWriter = new FileWriter(file, false);
serializer.setOutput(fileWriter);
serializer.startDocument("UTF-8", true);
[... Write data to the file....]

这就是我尝试从 XML 文件导入数据的方式。首先,我检查编码是否正确:

XmlPullParser parser = Xml.newPullParser();
FileReader reader = new FileReader(file);
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
parser.setInput(reader);
if(!"UTF-8".equals(parser.getInputEncoding())) {
    throw new IOException("Incorrect file encoding");
}
[... Read data from the file....]

我在这里遇到了一个问题。此代码在 Android 2.3.3(设备和模拟器)上运行良好,编码被正确检测为“UTF-8”。但在 API11+ 版本(Honeycomb、ICS、JB)上会抛出异常。当我在 Debug模式下运行它时,我可以看到 parser.getInputEncoding() 返回 null .我检查了 2.3.3 及更高版本生成的实际 XML 文件,它们具有完全相同的 header :<?xml version='1.0' encoding='UTF-8' standalone='yes' ?> . 为什么 getInputEncoding() 在 API11+ 上返回 null?

其他发现:

我发现有一种方法可以使用 FileInputStream 正确检测 API11+ 设备上的文件编码而不是 FileReader像这样:

XmlPullParser parser = Xml.newPullParser();
FileInputStream stream = new FileInputStream(file);
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
parser.setInput(stream, null);
if(!"UTF-8".equals(parser.getInputEncoding())) {
    throw new IOException("Incorrect file encoding");
}
[... Read data from the file....]

在这种情况下,getInputEncoding() 在 API11+ 模拟器和设备上正确检测到 UTF-8 编码,但它在 2.3.3 上返回 null。所以现在我可以在代码中插入一个分支,以在 API11+ 上使用 FileReader,在 API11 之前的版本上使用 FileInputStream:

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    parser.setInput(stream, null);
} else {
    parser.setInput(reader);
}

但是使用 XmlPullParser.getInputEncoding() 检查编码的正确方法是什么?为什么不同版本的 Android 会根据我使用的是 FileInputStream 还是 FileReader 而表现不同?

最佳答案

经过更多的尝试和错误,我终于设法弄清楚发生了什么。所以尽管 the documentation说:

Historically Android has had two implementations of this interface: KXmlParser via XmlPullParserFactory.newPullParser(). ExpatPullParser, via Xml.newPullParser().

Either choice is fine. The example in this section uses ExpatPullParser, via Xml.newPullParser().

现实情况是,在较旧的 API 上,例如 2.3.3 Xml.newPullParser() 返回 ExpatPullParser 对象。在 Ice Cream Sandwich 及更高版本上,它返回 KXmlParser 对象。正如我们从 this blog post 中看到的那样, Android 开发者从 2011 年 12 月就知道了:

In Ice Cream Sandwich we changed Xml.newPullParser() to return a KxmlParser and deleted our ExpatPullParser class.

...但从来没有费心去更新官方文档。

那么在 Ice Cream Sandwich 之前如何在 API 上检索 KXmlParser 对象?简单:

XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser parser = factory.newPullParser();

...事实上,这适用于所有版本的 android,新旧版本。然后向解析器的 setInput() 方法提供一个 FileInputStream,保留默认编码 null:

FileInputStream stream = null;
stream = new FileInputStream(file);
parser.setInput(stream, null);

在此之后,在 API 11 和更高版本上,您可以立即调用 parser.getInputEncoding(),它将返回正确的编码。但是在 API11 之前的版本中,除非您先调用 parser.next() ,否则它将返回 null,正如@Esailija 在他的回答中正确指出的那样。有趣的是,在 API11+ 上调用 next() 没有任何负面影响,因此您可以在所有版本上安全地使用此代码:

parser.next();
String encoding = parser.getInputEncoding();

这将正确返回“UTF-8”。

关于android - XmlPullParser.getInputEncoding() 在 API11+ 和 Android 的 API11 之前版本上的不同行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16009344/

相关文章:

java - 如何在 Android 中重置 AtomicInteger 唯一 ID?

c# - 使用 RestSharp 反序列化 XML 文档时出错

mysql - 如何处理数据以避免 MySQL "incorrect string value"错误?

java - Jaspersoft 工作室 : UTF-8 encoding issue

android - 以编程方式捕获网络流量(无根)

java - 为什么在关闭并重新打开对话框 fragment 后保留对象列表的值?

xml - 无法获取 XML 节点信息以追踪 :(

JavaScript 编码和解码错误

php - 通过MYSQL检索数据正确制作json对象

xml - xsd :simpleContent的含义