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

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

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

复制代码代码如下:

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

@Override
 public 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);
 //支持结束
}

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

复制代码代码如下:

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类型的属性强制指定用何种序列与反序列方法。然后我们来构造测试用例:

复制代码代码如下:

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”,我们指定了不同的序列化与反序列化方法。让我们来看烂这两个有什么不同:

复制代码代码如下:

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> {

@Override
 public 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);
 }

}

复制代码代码如下:

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> {

@Override
 public 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();
  }
 }
}

复制代码代码如下:

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> {

@Override
 public 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);
 }

}

复制代码代码如下:

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> {

@Override
 public 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细粒度更加高,加入了具体时间的属性。这在应用开发中是很常见的,生日信息我们往往知道年月日就可以了,而登陆时间往往需要得比较详细。从实例中我们可以知道,即便是同一类型,通过制定不同的序列与反序列方法,可以灵活地得到我们想要的数据形态。以上测试用例已经打包。点击下载

补充:

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

您可能感兴趣的文章:

时间: 2024-10-01 19:21:51

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

//随机生成 10到20条数据 数据包含 用户名(5-10位的字母) 性别 年龄(1-100岁)

//随机生成 10到20条数据 数据包含 用户名(5-10位的字母) 性别 年龄(1-100岁)/*生成随机数的方法*/function random(min, max) { if (min < 0 || max < 0) { console.log("请输入大于0的数据"); return false } if (max == null) {//判断如果只传入一个参数 var max = min;//将参数设置为最大值 min = 0;//将最小值设置为零 } retur

黑马day17 xstream生成xml数据&amp;json-lib生成json数据

1.XStream工具介绍: 这个工具即这个工具的jar包可以帮我们把JavaBean,集合(List,Set,Map)等生成xml格式的数据 jar包: xstream-1.4.4.jar 这是核心包 xpp3_min-1.1.4c.jar 这是必须依赖的包 只要导入这两个jar包 导入到构建路径中就可以使用了.. 2.json-lib介绍: 这个jar包可以帮我们把JavaBean,集合(List,Set,Map)等生成json格式的数据 jar包: 这是核心包 这是核心包的依赖包 为了方便

使用JSONObject类来生成json格式的数据

使用map构建json格式的数据 使用java bean来构建json对象

(转)iOS XML JSON SQLite CoreData 数据持久化

一.文件操作 1.因为应用是在沙盒(Sandbox)中的,在文件读写权限上受到限制,只能在几个目录下读写文件: * Documents:应用中用户数据可以放在这里,iTunes备份和恢复的时候会包括此目录 * tmp:存放临时文件,iTunes不会备份和恢复此目录,此目录下文件可能会在应用退出后删除 * Library/Caches:存放缓存文件,iTunes不会备份此目录,此目录下的文件不会在应用退出删除 2.相关方法:       # 使用NSSearchPathForDiretoriesI

Android Json生成及解析实例

JSON的定义: 一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性.业内主流技术为其提供了完整的解决方案(有点类似于正则表达式 ,获得了当今大部分语言的支持),从而可以在不同平台间进行数据交换.JSON采用兼容性很高的文本格式,同时也具备类似于C语言体系的行为. JSON Vs XML 1.JSON和XML的数据可读性基本相同 2.JSON和XML同样拥有丰富的解析手段 3.JSON相对于XML来讲,数据的体积小 4.JSON与JavaScript的交互更加方便 5.JSON对数据的

json 生成 json字符串

生成json格式的字符串. 需要相关包 1 package cn.itcast.controller; 2 3 import static org.junit.Assert.*; 4 5 import java.util.ArrayList; 6 import java.util.List; 7 8 import net.sf.json.JSONArray; 9 import net.sf.json.JSONObject; 10 import net.sf.json.JsonConfig; 11

iOS开发——网络Swift篇&amp;JSON与XML数据解析

JSON与XML数据解析 JSON数据解析(内置NSJSONSerialization与第三方JSONKit) 一,使用自带的NSJSONSerialization 苹果从IOS5.0后推出了SDK自带的JSON解决方案NSJSONSerialization,这是一个非常好用的JSON生成和解析工具,效率也比其他第三方开源项目高. NSJSONSerialization能将JSON转换成Foundation对象,也能将Foundation对象转换成JSON,但转换成JSON的对象必须具有如下属性

JQuery + JSON作为前后台数据交换格式实践

JQuery + JSON作为前后台数据交换 JQuery提供良好的异步加载接口AJAX,可以局部更新页面数据, http://api.jquery.com/category/ajax/ JSON作为一种轻量数据格式,被浏览器js引擎普遍支持,同xml格式. 使用AJAX+JSON数据格式来实现动态页面,有以下好处: 1. 松耦合, 页面HTML和数据彻底分离, 即表示层 和 数据层分开, 有利前台样式定制. 不同于以往后台脚本嵌套HTML标签,并输出数据到标签的合适位置, 来实现动态页面,表示

iOS开发-使用第三方库AFNetWorking解析JSON和XML数据

利用第三方库AFNetWorking解析网络请求的JSON和MXL数据具有很多方便的地方. 第三方库的下载地址:https://github.com/AFNetworking/AFNetworking 导入的包和宏定义 1 #import "ViewController.h" 2 #import "AFNetworking.h" 3 #import "GDataXMLNode.h" 4 5 //json地址 6 #define kJSONUrlS