如何让 SAX 解析器从 xml 声明中确定编码?

Posted

技术标签:

【中文标题】如何让 SAX 解析器从 xml 声明中确定编码?【英文标题】:Howto let the SAX parser determine the encoding from the xml declaration? 【发布时间】:2011-03-29 18:58:47 【问题描述】:

我正在尝试解析来自不同来源的 xml 文件(对此我几乎无法控制)。它们中的大多数都以 UTF-8 编码,使用以下 sn-p 不会导致任何问题:

SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
FeedHandler handler = new FeedHandler();
InputSource is = new InputSource(getInputStream());
parser.parse(is, handler);

由于 SAX 默认为 UTF-8,这很好。然而,一些文件声明:

<?xml version="1.0" encoding="ISO-8859-1"?>

即使声明了 ISO-8859-1,SAX 仍然默认为 UTF-8。 仅当我添加:

is.setEncoding("ISO-8859-1");

SAX 会使用正确的编码吗?

如何让 SAX 自动从 xml 声明中检测正确的编码,而无需我专门设置它?我需要这个,因为我事先不知道文件的编码是什么。

提前致谢, 艾伦

【问题讨论】:

【参考方案1】:

当您希望 Sax 自动检测编码时,使用 InputStream 作为 InputSource 的参数。

如果要设置特定的编码,请使用带有指定编码的Reader或setEncoding方法。

为什么?因为autodetection encoding algorithms 需要原始数据,而不是转换为字符。

主题中的问题是:如何让 SAX 解析器从 xml 声明中确定编码?我发现 Allan 对这个问题的回答具有误导性,因此我根据 Jörn Horstmann's评论和我后来的经历。

【讨论】:

是:关键点是,如果InputSource 是由InputStream 实例;如果从Reader 构造,它将不起作用(因为Reader 的意义在于它的输出是“解码后”)。即:new InputSource(getInputStream()) 是正确的。 附带说明,是否有任何库使用上述算法仅解析 XML 声明?我问是因为我不能直接使用 Sax,但我想从我的 xmls 中提取编码信息。 这应该是公认的解决方案。 InputStream 没有编码信息,因此 SAX 通过尝试从 XML 文件中读取编码属性来确定编码本身。这也适用于使用 XsltTransformer。 是否有可能获得xml序言的属性“编码”的确切内容? Xerces 定位器不起作用。【参考方案2】:

我自己找到了答案。

SAX 解析器在内部使用 InputSource 并来自 InputSource 文档:

SAX 解析器将使用 InputSource 对象确定如何 读取 XML 输入。如果有一个 可用的字符流,解析器 将直接读取该流, 忽略任何文本编码 在该流中找到的声明。如果 没有字符流,但是 有一个字节流,解析器 将使用该字节流,使用 InputSource 中指定的编码 否则(如果未指定编码) 自动检测字符编码 使用一种算法,例如 XML 规范。如果既不是 字符流也不是字节流 可用,解析器将尝试 打开到资源的 URI 连接 由系统标识符标识。

所以基本上你需要将一个字符流传递给解析器,以便它获取正确的编码。请参阅下面的解决方案:

SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
FeedHandler handler = new FeedHandler();
Reader isr = new InputStreamReader(getInputStream());
InputSource is = new InputSource();
is.setCharacterStream(isr);
parser.parse(is, handler);

【讨论】:

在不指定字符集的情况下构造 InputStreamReader 将使用您机器的默认字符集,可能是 iso-8859-1。正如您所引用的,当使用字符流时,xml 中的编码 decl 将被忽略,因此此代码仅适用于 iso-8859-1 文档。您的原始代码实际上应该可以工作,也许您可​​以将异常或您看到的确切问题添加到您的问题中。当使用字节流且未在 InputSource 上设置编码时,xml 解析器应自动检测编码,如 w3.org/TR/REC-xml/#sec-guessing 中所述。 如果我不使用“is.setCharacterStream()”,基本上我会得到一个无效的令牌异常。 这可能对你有用,但 Jörn 是对的。您引用的文档是相关且正确的。它告诉你 InputStream 的原始代码是正确的。该错误在文档本身中。如果您使用一种解决方法,例如覆盖编码或以 XML 规范以外的其他方式自动检测它,就像您使用 InputStreamReader 所做的那样,您应该记录这一事实。

以上是关于如何让 SAX 解析器从 xml 声明中确定编码?的主要内容,如果未能解决你的问题,请参考以下文章

在 JAVA 中使用 SAX 解析器从 XML 文件中提取文本节点

Java学习总结(21)——XML文档解析:DOM解析,SAX解析

SAX 解析器从 endelement 获取属性

使用 SAX 解析器确定是不是在叶节点

Dom4j常用解析函数

如何在 XML 中嵌入二进制数据?