本文出处:http://blog.csdn.net/chaijunkun/article/details/8257209,转载请注明。由于本人不定期会整理相关博文,会对相应内容作出完善。因此强烈建议在原始出处查看此文。

在年初的时候,我曾经写过一篇文章介绍非关系型数据库MongoDB和Jackson JSON框架相结合实现接口敏捷开发的文章(http://blog.csdn.net/chaijunkun/article/details/7263804),被可爱的CSDN小编推到了首页。在此本人对小编表示感谢。事隔10个月,随着手头一些项目的进行,对Jackson JSON框架用得越来越多。觉得有必要再写点什么补充出来。作为和广大同仁的一个经验的分享。

我们都知道,Jackson JSON以高速、方便和灵活著称。之前的文章中介绍过使用注解的形式来规定如何将一个对象序列化成JSON的方法,以及如何将一个JSON数据反序列化到一个对象上。但是美中不足的一点就是对于中文的处理。当然我说的美中不足是在默认情况下,Jackson JSON不会将中文等非ASCII字符转换为\uFFFF这样的形式来显示。也就是说默认情况下会显示为{"name":"张三"}而不是{"name":"\u5F20\u4E09"}。那么为什么有这样的需求呢?在HTTP协议中,我们可以指定数据头部分的内容编码。如:“GBK”、“UTF-8”等等。如果你设置正确了,那么OK,前者所表示的数据您可以正确处理。然而如果设置错误,对于中文字符将会产生乱码。两套应用系统对接,有可能两边使用的默认编码不同,如果一方修改默认编码将会对应用造成不可预知的后果。因此若能以长远的眼光开发,那么无论您设置成什么编码方式,都不会使数据产生乱码。因为,这里用到了万国编码——Unicode。

好的,问题出来了,我们如何解决呢?使其通过实验,Jackson JSON其实在默认设置下已经具备了对Unicode编码的JSON数据进行解析。所欠缺的就是在序列化对象时缺少相应的步骤。好在Jackson JSON框架允许我们自定义序列化方法。那么我们就来写一个序列化类:

package net.csdn.blog.chaijunkun.util;import java.io.IOException;import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.impl.JsonWriteContext;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.util.CharTypes;public class StringUnicodeSerializer extends JsonSerializer<String> {private final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();private final int[] ESCAPE_CODES = CharTypes.get7BitOutputEscapes();private void writeUnicodeEscape(JsonGenerator gen, char c) throws IOException {gen.writeRaw('\\');gen.writeRaw('u');gen.writeRaw(HEX_CHARS[(c >> 12) & 0xF]);gen.writeRaw(HEX_CHARS[(c >> 8) & 0xF]);gen.writeRaw(HEX_CHARS[(c >> 4) & 0xF]);gen.writeRaw(HEX_CHARS[c & 0xF]);}private void writeShortEscape(JsonGenerator gen, char c) throws IOException {gen.writeRaw('\\');gen.writeRaw(c);}@Overridepublic void serialize(String str, JsonGenerator gen,SerializerProvider provider) throws IOException,JsonProcessingException {int status = ((JsonWriteContext) gen.getOutputContext()).writeValue();switch (status) {case JsonWriteContext.STATUS_OK_AFTER_COLON:gen.writeRaw(':');break;case JsonWriteContext.STATUS_OK_AFTER_COMMA:gen.writeRaw(',');break;case JsonWriteContext.STATUS_EXPECT_NAME:throw new JsonGenerationException("Can not write string value here");}gen.writeRaw('"');//写入JSON中字符串的开头引号for (char c : str.toCharArray()) {if (c >= 0x80){writeUnicodeEscape(gen, c); // 为所有非ASCII字符生成转义的unicode字符}else {// 为ASCII字符中前128个字符使用转义的unicode字符int code = (c < ESCAPE_CODES.length ? ESCAPE_CODES[c] : 0);if (code == 0){gen.writeRaw(c); // 此处不用转义}else if (code < 0){writeUnicodeEscape(gen, (char) (-code - 1)); // 通用转义字符}else {writeShortEscape(gen, (char) code); // 短转义字符 (\n \t ...)}}}gen.writeRaw('"');//写入JSON中字符串的结束引号}}

这个序列化类将要对应用中所有使用Jackson JSON的地方全都用一种方法来处理字符串类型。光有了方法还不行,还要对它进行注册。让Jackson JSON在序列化对象的时候使用刚刚定义好的方法:

if (objectMapper== null){objectMapper= new ObjectMapper();//当找不到对应的序列化器时 忽略此字段objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);//使Jackson JSON支持Unicode编码非ASCII字符CustomSerializerFactory serializerFactory= new CustomSerializerFactory();serializerFactory.addSpecificMapping(String.class, new StringUnicodeSerializer());objectMapper.setSerializerFactory(serializerFactory);//支持结束
}

接下来我们来做一个测试用的对象,验证我们的代码:

package net.csdn.blog.chaijunkun.json;import java.util.Date;import net.csdn.blog.chaijunkun.util.DateDeserializer;
import net.csdn.blog.chaijunkun.util.DateSerializer;
import net.csdn.blog.chaijunkun.util.DateTimeDeserializer;
import net.csdn.blog.chaijunkun.util.DateTimeSerializer;import org.codehaus.jackson.annotate.JsonPropertyOrder;
import org.codehaus.jackson.map.annotate.JsonDeserialize;
import org.codehaus.jackson.map.annotate.JsonSerialize;@JsonPropertyOrder(alphabetic= false)
public class DemoObj {private Integer sid;private String stuName;private Boolean sex;@JsonSerialize(using= DateSerializer.class)@JsonDeserialize(using= DateDeserializer.class)private Date birthday;@JsonSerialize(using= DateTimeSerializer.class)@JsonDeserialize(using= DateTimeDeserializer.class)private Date logTime;//Getters and Setters}

从代码上可以看出,我们并没有对String类型的属性强制指定用何种序列与反序列方法。然后我们来构造测试用例:

package net.csdn.blog.chaijunkun.test;import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;import net.csdn.blog.chaijunkun.json.DemoObj;
import net.csdn.blog.chaijunkun.util.JSONUtil;import org.apache.log4j.Logger;public class JSONTest {private static Logger logger= Logger.getLogger(JSONTest.class);private static String json= "{\"sid\":2,\"stuName\":\"\u6C5F\u5357Style\",\"sex\":true,\"birthday\":\"2012-07-15\",\"logTime\":\"2012-12-04 19:22:36\"}";public static void main(String[] args) {DemoObj objSrc= new DemoObj();objSrc.setSid(1);objSrc.setStuName("鸟叔");objSrc.setSex(true);Calendar calendar= Calendar.getInstance();calendar.set(1977, Calendar.DECEMBER, 31, 0, 0, 0);objSrc.setBirthday(calendar.getTime());objSrc.setLogTime(new Date());logger.info(String.format("转换为JSON后的数据:%s", JSONUtil.toJSON(objSrc)));DemoObj objDes= JSONUtil.fromJSON(json, DemoObj.class);if(objDes==null){logger.info("反序列化失败");}else{logger.info("反序列化成功");SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");logger.info(String.format("标识:%d", objDes.getSid()));logger.info(String.format("姓名:%s", objDes.getStuName()));logger.info(String.format("性别:%s", objDes.getSex()==true?"男":"女"));logger.info(String.format("生日:%s", sdf.format(objDes.getBirthday())));logger.info(String.format("登录日期:%s", sdf.format(objDes.getLogTime())));}}}

看一下输出:

转换为JSON后的数据:{"sid":1,"stuName":"\u9E1F\u53D4","sex":true,"birthday":"1977-12-31","logTime":"2012-12-04 19:31:57"}
反序列化成功
标识:2
姓名:江南Style
性别:男
生日:2012-07-15 00:00:00
登录日期:2012-12-04 19:22:36

我们看到,已经成功将中文字符显示成为了Unicode编码的数据。同样,我们之前构造的Unicode编码的数据,在不经过任何修改的情况下成功显示出来了。

细心的朋友也许观察到了,在测试用的对象定义代码中,针对同样Date类型的属性“birthday”和“logTime”,我们指定了不同的序列化与反序列化方法。让我们来看烂这两个有什么不同:

package net.csdn.blog.chaijunkun.util;import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;public class DateTimeSerializer extends JsonSerializer<Date> {@Overridepublic void serialize(Date date, JsonGenerator gen, SerializerProvider provider)throws IOException, JsonProcessingException {SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String formattedDate= sdf.format(date);gen.writeString(formattedDate);}}
package net.csdn.blog.chaijunkun.util;import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.JsonDeserializer;public class DateTimeDeserializer extends JsonDeserializer<Date> {@Overridepublic Date deserialize(JsonParser parser, DeserializationContext context)throws IOException, JsonProcessingException {String dateFormat= "yyyy-MM-dd HH:mm:ss";SimpleDateFormat sdf= new SimpleDateFormat(dateFormat);try{String fieldData= parser.getText();return sdf.parse(fieldData);}catch (Exception e) {Calendar ca= Calendar.getInstance();ca.set(1970, Calendar.JANUARY, 1, 0, 0, 0);return ca.getTime();}}
}
package net.csdn.blog.chaijunkun.util;import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;public class DateSerializer extends JsonSerializer<Date> {@Overridepublic void serialize(Date date, JsonGenerator gen, SerializerProvider provider)throws IOException, JsonProcessingException {SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");String formattedDate= sdf.format(date);gen.writeString(formattedDate);}}
package net.csdn.blog.chaijunkun.util;import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.JsonDeserializer;public class DateDeserializer extends JsonDeserializer<Date> {@Overridepublic Date deserialize(JsonParser parser, DeserializationContext context)throws IOException, JsonProcessingException {String dateFormat= "yyyy-MM-dd";SimpleDateFormat sdf= new SimpleDateFormat(dateFormat);try{String fieldData= parser.getText();return sdf.parse(fieldData);}catch (Exception e) {Calendar ca= Calendar.getInstance();ca.set(1970, Calendar.JANUARY, 1, 0, 0, 0);return ca.getTime();}}
}

从代码我们可以看出,DateTimeSerializer和DateTimeDeserializer比DateSerializer和DateDeserializer细粒度更加高,加入了具体时间的属性。这在应用开发中是很常见的,生日信息我们往往知道年月日就可以了,而登陆时间往往需要得比较详细。从实例中我们可以知道,即便是同一类型,通过制定不同的序列与反序列方法,可以灵活地得到我们想要的数据形态。以上测试用例已经打包,上传到了我的资源。欢迎大家下载,共同学习。下载地址:http://download.csdn.net/detail/chaijunkun/4846394

2012年12月17日补充:

最近有一个需求,需要在序列化与反序列化对象的时候对数据进行修改,当发现数据源值为空时需要让生成的JSON显示改字段为“游客”。可是我无论如何指定序列化器与反序列化器都无效。程序根本走不到指定的代码中去。后来我得出结论,Jackson JSON在反序列化对象的时候,若JSON数据中对应属性为null,则不会走自定义的反序列化器;同样地,当你设置对象的某个属性值为null时,在将其序列化成JSON时,也不会走自定义的序列化器。因此若有类似的需求,请在序列化与反序列化之前通过硬代码形式判断和修改,千万不要什么事都指望着序列化器与反序列化器。

参考资料:来源于国外网站的一篇介绍如何转码的文章,原文有点错误。我将其改正了,并加入了一些中文注释:http://wiki.fasterxml.com/JacksonSampleQuoteChars

让Jackson JSON生成的数据包含的中文以unicode方式编码相关推荐

  1. java jackson unicode_如何让Jackson JSON生成的数据包含的中文以unicode方式编码

    我们都知道,Jackson JSON以高速.方便和灵活著称.之前的文章中介绍过使用注解的形式来规定如何将一个对象序列化成JSON的方法,以及如何将一个JSON数据反序列化到一个对象上.但是美中不足的一 ...

  2. Java - Jackson JSON Java Parser API

    文章目录 概述 依赖 Jackson JSON示例 基础数据 JSON转换为Java对象 Jackson JSON –将JSON转换为Map 概述 Jackson JSON Java Parser非常 ...

  3. swagger mock文档服务器,通过 Swagger 定义自动生成 Mock 数据

    我最近的在做的项目是一个前后端分离的项目,前后端由不同的团队分别开发,并且前端的进度经常领先后端.这就意味着,当前端在开发一个新功能时,API 可能还没有准备好.不过,我们会先和后端先商议好 API ...

  4. sql 生成csv数据_创建包含SQL Server数据的动态生成的CSV文件

    sql 生成csv数据 介绍 ( Introduction ) A few months back, I presented a paper at SQL Saturday 327 in Johann ...

  5. Jackson 解析j@JsonIgnorePropertiesson数据之忽略解析字段注解

    Jackson 解析j@JsonIgnorePropertiesson数据之忽略解析字段注解 我是一座离岛 关注 2017.04.12 14:32 字数 721 阅读 1121评论 0喜欢 0 以前解 ...

  6. JSON(生成和解析)

    一.1. 什么是JSON,JSON的作用是什么,如何编写JSON?   JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式.采用完全独立于编 ...

  7. JSON Schema校验数据

    参考 JSON Schema 规范(中文版)官方网站JSON Schema 对于数据对接系统来说,接口的数据入参校验尤为重要,使用javax.validation相关注解进行校验对于java对象的关联 ...

  8. Android Json生成及解析实例

    JSON的定义: 一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性.业内主流技术为其提供了完整的解决方案(有点类似于正则表达式 ,获得了当今大部分语言的支持),从而可以在不同平台间进行数据 ...

  9. Android中解析JSON形式的数据

    转自:http://shazhuzhu1.iteye.com/blog/974758 1.JSON(JavaScript Object Notation) 定义: 一种轻量级的数据交换格式,具有良好的 ...

最新文章

  1. 转挣扎的菜鸟 - 当OD不能装载也不能附加程序时
  2. redis源码epoll用法
  3. 无线持续攻击(wireless duration attack)
  4. 音视频技术开发周刊 | 199
  5. 90-20-010-源码-调试-Kylin-2.6.0源码调试
  6. php可以改jsp吗,把默认的php路径改成jsp
  7. [渝粤教育] 西南科技大学 农业技术经济学 在线考试复习资料
  8. 电脑文件误删除恢复的解决办法
  9. js初级动态随机抽人事件运用 计时器和超时期应用 解决用on事件onclick多次点击不能停止的问题
  10. QR码生成原理(一)
  11. excel函数应用:Column函数替代列参数提高公式灵活性
  12. 纯js图片验证码Captcha.js
  13. golang压缩图片
  14. 关于trunk的几个端口模式
  15. Java Data truncation:Incorrect xxx value: ‘xxx‘ for column ‘xxx‘ at row 1问题
  16. 若依前后端分离框架学习-2:登录过程
  17. 性格测试分析软件,4种性格测试系统
  18. BeanFactoryPostProcessor的调用时机
  19. 亚马逊测评需要注意哪些问题?新手小白一个怎么做?
  20. WIN7远程桌面连接--发生身份验证错误。要求的函数不受支持

热门文章

  1. java基础的正则表达式
  2. 深入理解C#的装箱和拆箱
  3. [置顶] 金山云存储解决企业办公难题
  4. 图解 Scala 基本语法 V2018.12.17
  5. PHP laravel框架Redis门面的误用
  6. idea git 注意事项
  7. LINQ to SQL 系列 如何使用LINQ to SQL插入、修改、删除数据
  8. BSCI—7:OSPF的路由汇总
  9. 给 console 添加颜色
  10. 洛谷 P3745 [六省联考2017]期末考试