如果有哪一個做程序員的小伙伴說自己沒有遇到中文亂碼問題,我是不愿意相信的。今天在做微信訂閱號的智能回復時,又一時迷亂的跳進了中文亂碼這個火坑。剛解決問題時,都歡呼雀躍了,完全忘記了她曾經帶給我的痛苦。
一、問題描述
看到沒,紅色框框內的亂碼赤裸裸的對我進行挑釁,而我卻無可奈何,真是糟糕透頂。
二、尋求解決之道
面對問題,只有拿著刀逼自己去解決啊,能怎么樣呢?
首先,必須搞清楚微信智能回復的機制,畫圖如下:
ps,工具用得不好,請見諒。
接下來,我們抓重點,看亂碼重要發生在什么位置。
1.controller返回給用戶
1
2
|
response.getOutputStream().write(result.getBytes()); |
就這段代碼了,指定response的編碼方式為UTF-8,按理說亂碼問題應該出現好轉,但是結果依然是沒有。
2.JAXB的toXML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
public String toXML(Object obj) { String result = null ; try { JAXBContext context = JAXBContext.newInstance(obj.getClass()); Marshaller m = context.createMarshaller(); m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8" ); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true ); m.setProperty(Marshaller.JAXB_FRAGMENT, true ); // 去掉報文頭 ByteArrayOutputStream os = new ByteArrayOutputStream(); XMLSerializer serializer = getXMLSerializer(os); m.marshal(obj, serializer.asContentHandler()); result = os.toString( "UTF-8" ); } catch (Exception e) { e.printStackTrace(); } logger.info( "response text:" + result); return result; } private XMLSerializer getXMLSerializer(OutputStream os) { OutputFormat of = new OutputFormat(); formatCDataTag(); of.setCDataElements(cdataNode); of.setPreserveSpace( true ); of.setIndenting( true ); of.setOmitXMLDeclaration( true ); of.setEncoding( "UTF-8" ); XMLSerializer serializer = new XMLSerializer(of); serializer.setOutputByteStream(os); return serializer; } |
這里有三個關鍵的點:
1. m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
2. getXMLSerializer(os)
3. os.toString("UTF-8");
可以看到以上三個地方均會涉及到轉碼,第1處,設置Marshaller的編碼;第二處,設置整個XMLSerializer的編碼;第三處,設置返回的ByteArrayOutputStream的string編碼。三處缺一不可。
這次這么透徹,應該解決了問題了吧,但是解決依然中文亂碼,那該如何是好呢?
3.tomcat的輸出環境作怪
針對這一點,網上有人提供這樣的解決思路。
set JAVA_OPTS=%JAVA_OPTS% %LOGGING_MANAGER% -Dfile.encoding=UTF-8
設置后重啟tomcat,問題是能夠解決,但副作用是整個tomcat在服務器上運行輸出(tomcat的cmd窗口)一直是亂碼,我認為這種方案不可取。
在運行的war中加入以下代碼
1
|
System.getProperty( "file.encoding" ); |
你會驚奇的發現,tomcat的運行環境(window server 2008)竟然是GBK,不知道你是否不驚奇,我是嚇到了,為什么不是UTF-8呢?如果是GBK的話,上面兩個步驟中我加入再多的UTF-8頁扯淡啊,不解。
三、解決問題
有了以上的經驗,我們修改以下wechat4j的代碼,主要是第二點。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
public String toXML(Object obj) { String result = null ; try { JAXBContext context = JAXBContext.newInstance(obj.getClass()); Marshaller m = context.createMarshaller(); String encoding = Config.instance().getJaxb_encoding(); logger.debug( "toXML encoding " + encoding + "System file.encoding " + System.getProperty( "file.encoding" )); m.setProperty(Marshaller.JAXB_ENCODING, encoding); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true ); m.setProperty(Marshaller.JAXB_FRAGMENT, true ); // 去掉報文頭 ByteArrayOutputStream os = new ByteArrayOutputStream(); XMLSerializer serializer = getXMLSerializer(os); m.marshal(obj, serializer.asContentHandler()); result = os.toString(encoding); } catch (Exception e) { e.printStackTrace(); } logger.info( "response text:" + result); return result; } private XMLSerializer getXMLSerializer(OutputStream os) { OutputFormat of = new OutputFormat(); formatCDataTag(); of.setCDataElements(cdataNode); of.setPreserveSpace( true ); of.setIndenting( true ); of.setOmitXMLDeclaration( true ); String encoding = Config.instance().getJaxb_encoding(); of.setEncoding(encoding); XMLSerializer serializer = new XMLSerializer(of); serializer.setOutputByteStream(os); return serializer; } |
這兩個方法中,對encoding我們加上可配置的編碼方式,可手動設置GBK(我的服務器上配置了GBK)、GB2312、UTF-8。
如此,會發現wechat4j的后臺輸出就不再是中文亂碼了,但返回給用戶的信息更亂了。
怎么能這樣呢,耍我這枚程序員啊,真想吐兩句臟話。但別怕啊,既然wechat4j的logger日志不再中文亂碼,那么只能說是第1個環節又出現問題了。
調整嘛
1
2
|
response.setHeader( "content-type" , "text/html;charset=UTF-8" ); // 瀏覽器編碼 response.getOutputStream().write(result.getBytes( "UTF-8" )); |
注意,這里不能是GBK,只能是UTF-8,我表示不清楚為什么,微信的產品經理給出來解釋下。
重點,JAXB和response合伙解決wechat4j中文亂碼的 方法再次聲明如下:
WeChatController.Java,就是你配給微信公眾開發平臺的URL處,response調整如下
1
2
|
response.setHeader( "content-type" , "text/html;charset=UTF-8" ); // 瀏覽器編碼 response.getOutputStream().write(result.getBytes( "UTF-8" )); |
wechat4j的JaxbParser.java,分別調整toXML(Object obj)和getXMLSerializer(OutputStream os)方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
public String toXML(Object obj) { String result = null ; try { JAXBContext context = JAXBContext.newInstance(obj.getClass()); Marshaller m = context.createMarshaller(); String encoding = Config.instance().getJaxb_encoding(); // GBK logger.debug( "toXML encoding " + encoding + "System file.encoding " + System.getProperty( "file.encoding" )); m.setProperty(Marshaller.JAXB_ENCODING, encoding); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true ); m.setProperty(Marshaller.JAXB_FRAGMENT, true ); // 去掉報文頭 ByteArrayOutputStream os = new ByteArrayOutputStream(); XMLSerializer serializer = getXMLSerializer(os); m.marshal(obj, serializer.asContentHandler()); result = os.toString(encoding); } catch (Exception e) { e.printStackTrace(); } logger.info( "response text:" + result); return result; } private XMLSerializer getXMLSerializer(OutputStream os) { OutputFormat of = new OutputFormat(); formatCDataTag(); of.setCDataElements(cdataNode); of.setPreserveSpace( true ); of.setIndenting( true ); of.setOmitXMLDeclaration( true ); String encoding = Config.instance().getJaxb_encoding(); //GBK of.setEncoding(encoding); XMLSerializer serializer = new XMLSerializer(of); serializer.setOutputByteStream(os); return serializer; } |
好了,萬事大吉了。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://blog.csdn.net/qing_gee/article/details/52788962