-------
1、在使用dom4j 对xml 文档进行解析时,通常会遇到把更新后的文档内容在写入到硬盘的xml 文档中,这时就存在一个编解码问题。如下 Book.xml 文档:
<?xml version="1.0" encoding="GBK" standalone="no"?>
<书架>
<书>
<书名 name="java" id="131314">Java就业培训教程</书名>
<作者 id="authorId">张孝祥</作者>
<售价>59.00元</售价>
</书>
</书架>
使用dom4j 对xml 文档进行解析代码如下:
//在书上添加一个售价节点:<售价>159.00元</售价>
@Test
public void add() throws Exception{
SAXReader reader = new SAXReader();
Document document = reader.read(new File("Book.xml"));
Element book = document.getRootElement().element("书");//获取第一本书节点
book.addElement("售价").setText("159.00元");
/*
* 更改后document 对象写入xml文件中,这里需要注意编码问题,解决方式有三种
*/
//1、按指定码表编码,Book.xml 码表会变为:UTF-8
//XMLWriter writer = new XMLWriter(new OutputStreamWriter(new FileOutputStream("Book.xml"), "UTF-8"));
//2、使用字节输出流写入数据,默认使用系统对UTF-8码表。Book.xml 码表会变为:UTF-8
//XMLWriter writer = new XMLWriter(new FileOutputStream("Book.xml"));
//3、使用格式化输入器,并更改xml文档的编码表。 createPrettyPrint:漂亮的格式化输入器
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("GBK");
//指定了格式化输入器,字符编码和字节编码都不会出现编码问题。
XMLWriter writer = new XMLWriter(new FileOutputStream("Book.xml"),format);
writer.write(document);
writer.close();
}
2、关于使用dom4j解析Book.xml 文档时编码问题的详细分析(MyEclipse默认为GBK码表)
- 情况1:XMLWriter writer = new XMLWriter(new FileWriter("Book.xml"));
若是通过字符流写入新内容,字符流会查找平台默认的编码表(GBK)来写入数据,数据写入后会将xml文档的码表更改为UTF-8,这时解析xml文档就会使用UTF-8来解析。所以我们看到的就是乱码。
注:UTF-8 和GBK 不能互转码,也不能逆转码。 - 情况2: writer = new XMLWriter(new OutputStreamWriter(new FileOutputStream("Book.xml"), "UTF-8"));
通过字符转换流来指定写入文档的编码模式,可以解决乱码问题,但是这样会改变元xml 文档的编码模式,即改为:encoding="UTF-8" 。 - 情况3: writer = new XMLWriter(new OutputStreamWriter(new FileOutputStream("Book.xml"), "GBK"));
这种指定编码方式错误, 写入数据时使用的是GBK编码表,写入后xml文档码表会更改为UTF-8,则文档解码时使用的是UTF-8来解码,会出现乱码。这种方式同情况1。 - 情况4: writer = new XMLWriter(new FileOutputStream("Book.xml"));
该方法是直接通过字节流来处理,所以不会存在编码解码问题。
因为本身就是字节。向xml写入数据时使用的是UTF-8的码表。
总结1:通过上述分析,可以说明数据写入时使用的码表和解码时使用的码表不同就会造成乱码问题。所以只有输出流编码方式为UTF-8 的方式下才不会出现乱码。但所有方式都会改变原xml文档的编码表。改为:encoding="UTF-8"。
为了解决保持xml文档编码表不变,dom4j引入了格式化输入器,通过格式化输入器,能够实现按指定的编码表来编码,也可以把xml文档的码表改为指定的码表。
- 情况1:格式化输入器指定编码与xml文档编码模式相同时(都是GBK的情况下)。
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("GBK");
使用字符输出流写入: writer = new XMLWriter(new FileWriter("Book.xml"),format);
使用字节输出流写入: writer = new XMLWriter(new FileOutputStream("Book.xml"),format);
这两种写入方式都正确。但有区别:前者是根本就不会使用格式化输入器指定的GBK编码表编码,而是查找平台默认的编码表来编码。前者会按格式化输入器指定的编码表来编码。更新后xml文档的编码表不变。 - 情况2:xml文档的编表和格式化输入器指定码表不相同时:format.setEncoding("UTF-8");
使用字符输出流写入: writer = new XMLWriter(new FileWriter("Book.xml"),format);
使用字节输出流写入: writer = new XMLWriter(new FileOutputStream("Book.xml"),format);
使用字节输入流写入正确,使用字符输入流会出现乱码,但两者都会将原Book.xml文档的编码表改为encoding="UTF-8" 。
总结2:通过格式化输入器来指定码表的方式写入数据后,都会将原xml文档的编码表更改为指定的码表。因此只要我们指定的码表与xml文档的码表相同,在写入数据时都不会更改xml文档的默认码表。但是如果使用字符输入流写入数据时,一定要保证平台默认码表与xml文档码表一致,这样写入数据时不会出现乱码。在任何情况下使用字节输入流写入数据都不会出现乱码。
下面把 Book.xml 文档、MyEclipse 开发平台和format格式化输入器的指定码表在GBK和UTF-8之间交叉变换,测试后的情况如下表:表中红×表示出现乱码。
分析表中的两种情况:当三种码表都是GBK时,输出流编码都是使用的GBK码表,同时新的Book.xml 解码方式也是GBK,两者匹配。若仅仅format 指定编码为UTF-8 ,但是字符输出流编码为GBK,而新的Book.xml 解码方式是GBK,两者不匹配,出现乱码。
注意:字符输出流编码只跟平台默认码表有关,字节输出流随指定情况而定,若没有指定码表,则由系统的指定码表限制。
总结:表中说明了一个问题,即编码和解码使用的码表必须一致,否则出现乱码。这句话说起来简单。但正真用于实际分析时,却不是那么好把握。特别是在使用字符输出流写入数据时,因为字符输出流使用的编码表永远都是开发平台默认的码表,且更新后xml文档的解码方式会改为UTF-8(若有转换器指定码表则由转换器来指定xml文档变更后的码表),这样很容易出现乱码问题。