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

一、问题描述

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

二、寻求解决之道

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

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

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;
}

好了,万事大吉了。

为JAXB和response设置编码,解决wechat4j中文乱码的更多相关文章

  1. Servlet学习(五)——通过response设置响应体及中文乱码问题

    1.响应体设置文本 PrintWriter writer=response.getWriter(); 获得字符流,通过字符流的write(String s)方法可以将字符串设置到response 缓冲 ...

  2. Linux Centos7设置UTF-8编码,防止中文乱码

    Linux Centos7设置UTF-8编码,防止中文乱码 # localeLANG=zh_CN.gb2312LC_CTYPE="zh_CN.gb2312"LC_NUMERIC=& ...

  3. Ubuntu14.04安装中文输入法以及解决Gedit中文乱码问题

    1 设置中文显示环境 1. 打开System Settings 2. 打开Personal-> Language Support. 会弹出如下对话框,提示你“语言支持没安装完整”. 点击“Rem ...

  4. Ubuntu14.04安装中文输入法以及解决Gedit中文乱码问题[转载]

    转载自:http://www.cnblogs.com/zhcncn/p/4032321.html 写在前面:解决gedit 在txt文件格式出现乱码的问题,在我自己的操作中是需要把系统设置成中文显示环 ...

  5. 解决mysql中文乱码问题?

    mysql是我们项目中非常常用的数据型数据库.但是因为我们需要在数据库保存中文字符,所以经常遇到数据库乱码情况.下面就来介绍一下如何彻底解决数据库中文乱码情况. 1.中文乱码 1.1.中文乱码 cre ...

  6. 彻底解决mysql中文乱码

    mysql是我们项目中非常常用的数据型数据库.但是因为我们需要在数据库保存中文字符,所以经常遇到数据库乱码情况.下面就来介绍一下如何彻底解决数据库中文乱码情况. 1.中文乱码 1.1.中文乱码 cre ...

  7. 如何解决PHP中文乱码问题

    如何解决PHP中文乱码问题 一.解决HTML中中文乱码问题方法    1.在head标签里面加入UTF8编码(国际化编码):UTF-8是没有国家的编码,也就是独立于任何一种语言,任何语言都可以使用的. ...

  8. 解决URL中文乱码问题--对中文进行加密、解密处理

    解决URL中文乱码问题--对中文进行加密.解密处理 情景:在资源调度中,首先用户需要选择工作目标,然后跟据选择的工作目标不同而选择不同的账号和代理ip.处理过程如下:点击选择账号,在js中获取工作目标 ...

  9. request.getParameter()及解决数据库中文乱码问题——实习第七天

    今天老师让我们自己做一个小项目,我开始着手于实现这个小项目.途中遇到过几个小问题,在此做个小记录, 相信后期还是会借鉴的. 1,从前台传入数据给后台传入数据,并没有传入成功: 输出的为Null. 当然 ...

随机推荐

  1. 两天学会DirectX 3D之第二天

    提要 前几天非常easy地跑了一个DirectX 9 程序,以为DirectX就那么绘制,事实证明有点Naive了. 之前的那个程序最多也就是个固定流水线的东西. 可是今天要用DirectX11来写一 ...

  2. Jmeter_远程启动 I

    Jmeter 是Java 应用,对于CPU和内存的消耗比较大,因此,当需要模拟数以千计的并发用户时,使用单台机器模拟所有的并发用户就有些力不从心,甚至会引起JAVA内存溢出错误. 其实,Jmeter的 ...

  3. 对IOS设备中UDID的一些思考

    本文转载至 http://blog.csdn.net/happyrabbit456/article/details/11565209 http://blog.csdn.net/xiaoguan2008 ...

  4. 用Java自定义一个定时器

    1.先定义一个监听类: import java.util.Date; import java.util.Timer; import javax.servlet.ServletContextEvent; ...

  5. Android 切换主题以及换肤的实现

    Android 切换主题以及换肤的实现 一.介绍 现在市面上有很多 APP 有切换主题和皮肤的功能!特别是阅读类的 APP! 上面两张图分别是 知乎 APP 和Fuubo APP的两张截图!都带有切换 ...

  6. poj1325(Machine Schedule)

    题目链接:传送门 题目大意:有k个任务,可以在 A 机器的 x 位上完成,也可以在 B 机器的 y 位上完成.问最少需要多少个点位即可完成所有任务. 题目思路:求最小点覆盖. 把 A 机器,B 机器看 ...

  7. 【BZOJ4621】Tc605 DP

    [BZOJ4621]Tc605 Description 最初你有一个长度为 N 的数字序列 A.为了方便起见,序列 A 是一个排列. 你可以操作最多 K 次.每一次操作你可以先选定一个 A 的一个子串 ...

  8. ES6入门概览一

    一.let const 1.let命令:声明变量仅在块级作用域有效:let实际上为js新增了块级作用域 好处: -不存在变量提升: -内部声明的变量 函数 不会影响外部 -不可重复声明变量 2.con ...

  9. Spring Security OAuth2 授权失败(401) 问题整理

    Spring Cloud架构中采用Spring Security OAuth2作为权限控制,关于OAuth2详细介绍可以参考 http://www.ruanyifeng.com/blog/2014/0 ...

  10. JavaScript学习笔记-构造函数

    什么是构造函数 简单说构造函数是类函数,函数名与类名完全相同,且无返回值.构造函数是类的一个特殊成员函数. JavaScript构造函数 * 在JavaScript的世界里没有类的概念,JavaScr ...