动态的SAX解析器用于UTF-8或ISO-8859-1编码的XML

5

我正在开发一款Android应用程序,需要解析不同的XML文件。其中大部分文件采用UTF-8编码,但是少数文件可能采用ISO-8859-1编码。

  HttpURLConnection con = (HttpURLConnection) url.openConnection();
  ...
  in = con.getInputStream();
  InputSource is = new InputSource(in);
  ...
  parser.parse(is, handler);

我用于处理输入的代码如上所示。关于InputSourcejava文档中写道:

如果没有字符流但是有字节流,则解析器将使用该字节流,并使用InputSource中指定的编码(如果未指定编码,则使用诸如XML规范中所述的算法自动检测字符编码)。

我传入了一个ByteStream,没有指定编码,因此根据文档应该自动检测编码。但实际上并没有这样做。所有以UTF-8编码的文件都可以正常工作,但ISO-8859-1编码的文件不行(我得到了一个Parser Expat... Exception for some invalid characters)。如果我手动将InputSource的编码设置为"ISO-8859-1",情况会反过来。

我该怎么解决呢?我在Google和Stackoverflow上搜索了数小时,但仍然找不到解决方案。我也尝试将CharacterStream传递给InputSource,但ISO-8859-1文件中的一些字符(äöüÄÖÜß)仍然在我的应用程序中显示为"?"。

提前致谢!

4个回答

1
我建议检查是否存在旧的ASCII字符集中没有的字符,如果存在UTF-8字符,则重新编码字符串:
String output=new String(input.getBytes("8859_1"), "utf-8");

该行代码将 ISO-8859-1 编码转换为 Java 使用的 utf-8 编码。


1
最佳解决方案取决于您问题的确切原因。如果您通过HTTP检索XML文档,则编码也可以在Content-Type响应标头中指定,而不一定在XML文档本身中指定。如果是这种情况,并且Android中的XML库已正确实现(我无法在此处检查Content+Type标头是否被评估),则您应该能够直接使用URL创建InputSource new InputSource("http://...");
如果编码未在HTTP标头中设置并且未在XML序言中指定,则如果解析器假定UTF-8编码(如XML规范所要求),则解析器将正常运行。文档中提到的自动检测并不意味着解析器实际上会查看文档内容以对编码进行假设,而是表示它会检查XML流的编码属性。如果编码属性缺失,则默认为UTF-8。

未声明编码的类似问题是错误声明编码,因此他仍然必须使用try catch。 - Esailija
编码参数设置为 "UTF-8" 或 "ISO-8859-1"。但似乎 SAX 解析器/输入源没有处理这些信息。我通过 HTTP 检索 XML,但检查响应的内容类型是个好主意,我会尝试一下。 - Marius5000
最终我检查了HTTP头的Content-Type。如果字符集是“ISO-8859-1”,我就设置InputSource的编码。否则,我不设置编码,使用默认的“UTF-8”。效果很好。 - Marius5000

0
最直接的方法是使用UTF-8,如果解析器抛出无效字节的异常,则尝试将其重新解析为Windows-1252。选择1252是因为我怀疑你不会看到任何人使用ISO-8859-1 C1字符,而你会看到人们使用Windows 1252字符并声称它是ISO-8859-1。

-1

我建议让SAX决定编码,它可以从XML声明的编码属性中知道。

<?xml version="1.0" encoding="utf-8"?>

注意:如果没有xml声明,这是合法的,则假定编码为UTF-8

如果您使用字节流InputSource,就像您的示例一样,并且未明确设置InputStream编码,则SAX将从XML中获取编码

更新

尝试这个测试。它将xml字符串写入iso-8859-1的1.xml文件中。然后SAX解析它并打印根元素文本(它只是一个字符'ä')。SAX应该理解1.xmk使用iso-8859-1,否则输出将会失真。

String xml = "<?xml version='1.0' encoding='iso-8859-1'?><root>ä</root>";
OutputStreamWriter wrt = new OutputStreamWriter(new FileOutputStream(
        "1.xml"), "iso-8859-1");
wrt.write(xml);
wrt.close();
SAXParserFactory sf = SAXParserFactory.newInstance();
SAXParser p = sf.newSAXParser();
p.parse(new FileInputStream("1.xml"), new DefaultHandler() {
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        System.out.println((int)ch[start]);
        System.out.println(String.valueOf(ch, start, length));
    }
});

查看输出

228
ä

没错。SAX 理解 XML 编码 = 'iso-8859-1'。


“让SAX决定”是什么意思?所有的XML文件都有一个编码属性。我还以为InputSource会根据编码属性来决定编码方式。但它并没有这样做。你有其他的想法如何处理这个问题吗? - Marius5000
Sax 无法检测当前编码。它会忽略 XML 头部中的任何编码设置。如果编码错误,您必须自行更正。请参见关于此问题的我的答案。 - rekire
ä是229(0xE5)。在解析器中获取XML后,在调试器中找到这个字母并检查它是否为229。 - Evgeniy Dorofeev
原因是Java通常使用UTF-8编码。如果您的输入文件有其他编码(这可能会发生),则需要重新编码该文件。 - rekire
我不知道你是怎么做到的Evgeniy,但在执行这个代码后我收到了一个异常警告:“格式不正确(无效令牌)”。 - DennisVA
显示剩余3条评论

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