谈谈为JAXB和response设置编码,解决wechat4j中文乱码的问题

网友投稿 530 2023-06-23


谈谈为JAXB和response设置编码,解决wechat4j中文乱码的问题

如果有哪一个做程序员的小伙伴说自己没有遇到中文乱码问题,我是不愿意相信的。今天在做微信订阅号的智能回复时,又一时迷乱的跳进了中文乱码这个火坑。刚解决问题时,都欢呼雀跃了,完全忘记了她曾经带给我的痛苦。

一、问题描述

看到没,红色框框内的乱码赤裸裸的对我进行挑衅,而我却无可奈何,真是糟糕透顶。

二、寻求解决之道

面对问题,只有拿着刀逼自己去解决啊,能怎么样呢?

首先,必须搞清楚微信智能回复的机制,画图如下:

ps,工具用得不好,请见谅。

接下来,我们抓重点,看乱码重要发生在什么位置。

1.controller返回给用户

response.setHeader("content-type", "text/html;charset=UTF-8");// 浏览器编码

response.getOutputStream().write(result.getBytes());

就这段代码了,指定response的编码方式为UTF-8,按理说乱码问题应该出现好转,但是结果依然是没有。

2.JAXB的toXML

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中加入以下代码

System.getProperty("file.encoding");

你会惊奇的发现,tomcat的运行环境(window server 2008)竟然是GBK,不知道你是否不惊奇,我是吓到了,为什么不是UTF-8呢?如果是GBK的话,上面两个步骤中我加入再多的UTF-8页扯淡啊,不解。

三、解决问题

有了以上的经验,我们修改以下wechat4j的代码,主要是第二点。

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个环节又出现问题了。

调整嘛

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调整如下

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)方法:

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;

}

好了,万事大吉了。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:原生的强大DOM选择器querySelector介绍
下一篇:Java中分割字符串的两种方法实例详解
相关文章

 发表评论

暂时没有评论,来抢沙发吧~