欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2024-03-08 23:33:06
如果有哪一个做程序员的小伙伴说自己没有遇到中文乱码问题,我是不愿意相信的。今天在做微信订阅号的智能回复时,又一时迷乱的跳进了中文乱码这个火坑。刚解决问题时,都欢呼雀跃了,完...

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

一、问题描述

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

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

二、寻求解决之道

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

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

谈谈为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的后台输出就不再是中文乱码了,但返回给用户的信息更乱了。

谈谈为JAXB和response设置编码,解决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中文乱码的问题

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。