一、“中文問題沒商量”之Dom4j中的編碼問題
本文主要講述的是Dom4j在把Document儲存到檔案過程中出現的一個中文問題,本文跟《
80前》一文一樣,以Spring項目無關,請“春迷”們自重、沒事勿擾,文中不足之處歡迎大家批評指教。
Dom4j是一個比較優秀的java開源xml解析項目,支援DOM, SAX and JAXP.,并提供對XPath查詢語言的強大支援。是以,在EasyJF團隊的很多開源項目中,如EasyJWeb、EasyDBO等都是使用Dom4j來處理xml檔案相關操作。
1、從一個xml檔案中載入一個Dom到記憶體:
FileInputStream in = new FileInputStream(new File(fileName));
SAXReader reader = new SAXReader();
doc = reader.read(in);
2、把Dom中的資料寫入到xml檔案中
使用Dom4j,要把一個Dom中的資料寫入到檔案非常簡單,API如下:
public void write(Writer writer) throws IOException;
是以,假如我們要把一個Document寫入到c:\test.xml檔案中,可以簡單的使用下面的代碼即可:
java.io.Writer wr= new java.io.FileWrite(filename);
doc.write(wr);
wr.close();//注意,必須要執行close()方法,才會實作真正的寫入
這種用法也是Dom4j所推薦我們使用的非常簡單的方法。然而,當我們的dom中包含有中文字元資料的時候,這種方法寫入的xml文檔卻無法使直覺打開。會提示類似如下的錯誤:
org.dom4j.DocumentException: invalid byte 1 of 1-byte UTF-8 sequence (0xb2) Nested exception: invalid byte 1 of 1-byte UTF-8 sequence (0xb2)
at org.dom4j.io.SAXReader.read(SAXReader.java:484)
at org.dom4j.io.SAXReader.read(SAXReader.java:343)
at
我們可以看生成的xml檔案編碼,内容是utf-8的,但檔案格式确是ANSI的,如下圖所示:
原因分析:
由于FileWriter預設的輸出編碼是ANSI編碼,而Dom4j中的wirte方法提供的内容實際是以UTF-8儲存的,是以造成了包括中文字元的XML檔案無法正常閱讀。查了半天代碼,最後才發現:是UTF字元的問題。當XML中含有中文,而沒有指定XML Encoding="UTF-8"的時候,就會産生這樣的錯誤。
解決方法:
不能使用簡單的FileWriter,而應該是使用一個能指定具體輸出編碼的Writer,在JDK的io包中, OutputStreamWriter可以指定輸出編碼。
正确的代碼如下:
java.io.OutputStream out=new java.io.FileOutputStream(fileName);
java.io.Writer wr=new java.io.OutputStreamWriter(out,"UTF-8");
doc.write(wr);
wr.close();
out.close();
簡化一下可以寫成下面的樣式:
java.io.Writer wr=new java.io.OutputStreamWriter(new java.io.FileOutputStream(fileName),"UTF-8");
小結:
由于大多數優秀的基礎性開源項目都是老外開發,他們不大可能在中文平台下進行測試,用例資料也很少會使用中文平台,是以,我們即使按照這些開源項目的通用說明文檔及使用者指南去操作,也會出現很多不可預知的錯誤。這也是為什麼本人要參與組建開源團隊EasyJF,提倡搞國産開源,并開發一些基礎性的開源架構如EasyJWeb、EasyDBO的一個初衷。
當然,這裡提出的中文問題,算是一個還“沒來得及商量”以及要通過一些罕見的處理才能正确運作的中文問題。是以,同樣歸并到了“中文問題沒商量”系列中。
(注:本文作者,
EasyJF開源團隊大峽
,轉載請保留作者聲明!)
二、DOM4J中文問題 Invalid byte 1 of 1-byte UTF-8 sequence
http://hlwlemon.blog.sohu.com/81841115.html 本文出處
在用dom4j的時候發現有時會出現這個問題:無法以UTF-8儲存xml檔案,儲存後再次讀出的時候會報“Invalid byte 2 of 2-byte UTF-8 sequence.”這樣一個錯誤,檢查發現由dom4j生成的這個檔案,在使用可正确處理XML編碼的任何的編輯器中中文成亂碼,從記事本檢視并不會出現亂碼會正确顯示中文。讓我很是頭痛。。。。
試着使用GBK、gb2312編碼來生成的xml檔案卻可以正常的被解析。是以懷疑的dom4j沒有對utf-8編碼進行處理。便開始檢視dom4j的原代碼。終于發現的問題所在,是自己程式的問題。
在dom4j的範例中建立一個xml文檔的代碼都類似如下
public void createXML(String fileName) {
Document doc = org.dom4j.DocumentHelper.createDocument();
Element root = doc.addElement("book");
root.addAttribute("name", "我的圖書");
Element childTmp;
childTmp = root.addElement("price");
childTmp.setText("21.22");
Element writer = root.addElement("author");
writer.setText("李四");
writer.addAttribute("ID", "001");
try {
org.dom4j.io.XMLWriter xmlWriter = new org.dom4j.io.XMLWriter(
new FileWriter(fileName));
xmlWriter.write(doc);
xmlWriter.close();
}
catch (Exception e) {
System.out.println(e);
}
在上面的代碼中輸出使用的是FileWriter對象進行檔案的輸出。這就是不能
正确進行檔案編碼的原因所在,java中由Writer類繼承下來的子類沒有提供編碼
格式處理,是以dom4j也就無法對輸出的檔案進行正确的格式處理。這時候所保
存的檔案會以系統的預設編碼對檔案進行儲存,在中文版的window下java的預設
的編碼為GBK,也就是所雖然我們辨別了要将xml儲存為utf-8格式但實際上檔案
是以GBK格式來儲存的,是以這也就是為什麼能夠我們使用GBK、GB2312編碼來生
成xml檔案能正确的被解析,而以UTF-8格式生成的檔案不能被xml解析器所解析
的原因。
好了現在我們找到了原因所在了,我們來找解決辦法吧。首先我們看看dom4j
是如何實作編碼處理的
public XMLWriter(OutputStream out) throws UnsupportedEncodingException {
//System.out.println("In OutputStream");
this.format = DEFAULT_FORMAT;
this.writer = createWriter(out, format.getEncoding());
this.autoFlush = true;
namespaceStack.push(Namespace.NO_NAMESPACE);
public XMLWriter(OutputStream out, OutputFormat format) throws UnsupportedEncodingException {
//System.out.println("In OutputStream,OutputFormat");
this.format = format;
protected Writer createWriter(OutputStream outStream, String
encoding) throws UnsupportedEncodingException {
return new BufferedWriter(
new OutputStreamWriter( outStream, encoding )
);
由上面的代碼我們可以看出dom4j對編碼并沒有進行什麼很複雜的處理,完全通過java本身的功能來完成。是以我們在使用dom4j的來生成我們的XML檔案時不應該直接為在建構XMLWriter時,不應該直接為其賦一個Writer對象,而應該通過一個OutputStream的子類對象來建構。也就是說在我們上面的代碼中,不應該用FileWriter對象來建構xml文檔,而應該使用FileOutputStream對象來建構,是以将代碼修改入下:
public void createXML(String fileName) {
//注意這裡的修改
new FileOutputStream(fileName));
這樣生成的代碼就是中文的了
異常:Invalid byte 1 of 1-byte UTF-8 sequence.
三、dom4j 中文處理問題編碼轉換
http://topic.csdn.net/t/20040329/17/2900049.html
正好昨天我遇到這個問題,如此解決:
SAXReader reader = new SAXReader();
org.dom4j.Document document = reader.read("D:\\ha.xml");
OutputFormat of = new OutputFormat();
of.setEncoding("gb2312"); //改變編碼方式
XMLWriter writer = new XMLWriter(new FileWriter "d:\\dom4j.xml"), of);
List list = document.selectNodes(
"//PersonId[@name = 'chen_zhen']");
if (list.size() > 0)
{
org.dom4j.Element e = (org.dom4j.Element)list.get(0);
System.out.println(e.toString());
e.setAttributue("name", "huo_yuanjia");
e.setAttributue("age", "20");
e.setName("Fighter"); //改變tag
e.setText("0001"); //改變value
}
writer.write(document);
writer.close();
以下函數可以實作編碼轉換:
public void writeXML(String file,Document document,String encoding){
try{
FileWriter out = new FileWriter(new File(file));
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding(encoding == null ? format.getEncoding() : encoding);
XMLWriter writer = new XMLWriter(out, format);
writer.close();
catch(IOException ex){
ex.printStackTrace();
四、dom4j 輸出UTF-8 XML時中文亂碼
http://royboy.javaeye.com/blog/337948
使用DOM4J的XMLWriter輸出UTF-8編碼的XML檔案時,出現亂碼。
首先,設定輸出的編碼,在這我們使用“utf-8”
Java代碼
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("utf-8");
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("utf-8");
輸出代碼:
try {
output = new XMLWriter(new FileWriter("entity.xml"), format);
output.write(document);
output.close();
} catch (IOException e) {
e.printStackTrace();
try {
output = new XMLWriter(new FileWriter("entity.xml"), format);
output.write(document);
output.close();
} catch (IOException e) {
e.printStackTrace();
上面的輸出如果有中文,可以會出現亂碼的問題,将上面的FileWriter改成FileOutputStream便可以了。
output = new XMLWriter(new FileOutputStream("entity.xml"), format);
五、dom4j中文指定XML編碼為GBK(注:指定為utf-8時仍會出現編碼問題,見上面四)
1、檔案形式:
XMLWriter writer = null;
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("GBK");
try{
writer= new XMLWriter(new FileWriter(new File("test.xml")),format);//用FileOutputStream更好。
writer.write(document);
}
catch(IOException ioe){ioe.printStackTrace();}
2、String形式,我現在做的架構中,是PO<-->XML--Servlet,不需要生成檔案,搜尋了半天沒搜尋出來,後來幹脆自己試出來了。
StringWriter sw=new StringWriter();
XMLWriter writer = null;
writer=new XMLWriter(format);
writer.setWriter(sw);
writer.write(document);
System.out.println(sw.toString());