配色: 字号:
如何让Jackson JSON生成的数据包含的中文以unicode方式编码
2016-10-15 | 阅:  转:  |  分享 
  
如何让JacksonJSON生成的数据包含的中文以unicode方式编码

这篇文章主要介绍了如何让JacksonJSON生成的数据包含的中文以unicode方式编码。需要的朋友可以过来参考下,希望对大家有所帮助

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

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

复制代码代码如下:

importjava.io.IOException;

importorg.codehaus.jackson.JsonGenerationException;importorg.codehaus.jackson.JsonGenerator;importorg.codehaus.jackson.JsonProcessingException;importorg.codehaus.jackson.impl.JsonWriteContext;importorg.codehaus.jackson.map.JsonSerializer;importorg.codehaus.jackson.map.SerializerProvider;importorg.codehaus.jackson.util.CharTypes;

publicclassStringUnicodeSerializerextendsJsonSerializer{

privatefinalchar[]HEX_CHARS="0123456789ABCDEF".toCharArray();privatefinalint[]ESCAPE_CODES=CharTypes.get7BitOutputEscapes();

privatevoidwriteUnicodeEscape(JsonGeneratorgen,charc)throwsIOException{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]);}

privatevoidwriteShortEscape(JsonGeneratorgen,charc)throwsIOException{gen.writeRaw(''\\'');gen.writeRaw(c);}

@Overridepublicvoidserialize(Stringstr,JsonGeneratorgen,SerializerProviderprovider)throwsIOException,JsonProcessingException{intstatus=((JsonWriteContext)gen.getOutputContext()).writeValue();switch(status){caseJsonWriteContext.STATUS_OK_AFTER_COLON:gen.writeRaw('':'');break;caseJsonWriteContext.STATUS_OK_AFTER_COMMA:gen.writeRaw('','');break;caseJsonWriteContext.STATUS_EXPECT_NAME:thrownewJsonGenerationException("Cannotwwww.visa158.comuehere");}gen.writeRaw(''"'');//写入JSON中字符串的开头引号for(charc:str.toCharArray()){if(c>=0x80){writeUnicodeEscape(gen,c);//为所有非ASCII字符生成转义的unicode字符}else{//为ASCII字符中前128个字符使用转义的unicode字符intcode=(c
}

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

复制代码代码如下:

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

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

复制代码代码如下:

importjava.util.Date;

importnet.csdn.blog.chaijunkun.util.DateDeserializer;importnet.csdn.blog.chaijunkun.util.DateSerializer;importnet.csdn.blog.chaijunkun.util.DateTimeDeserializer;importnet.csdn.blog.chaijunkun.util.DateTimeSerializer;

importorg.codehaus.jackson.annotate.JsonPropertyOrder;importorg.codehaus.jackson.map.annotate.JsonDeserialize;importorg.codehaus.jackson.map.annotate.JsonSerialize;

@JsonPropertyOrder(alphwww.hunanwang.netabetic=false)publicclassDemoObj{privateIntegersid;privateStringstuName;privateBooleansex;@JsonSerialize(using=DateSerializer.class)@JsonDeserialize(using=DateDeserializer.class)privateDatebirthday;@JsonSerialize(using=DateTimeSerializer.class)@JsonDeserialize(using=DateTimeDeserializer.class)privateDatelogTime;

//GettersandSetters}

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

复制代码代码如下:

importjava.text.SimpleDateFormat;importjava.util.Calendar;importjava.util.Date;

importnet.csdn.blog.chaijunkun.json.DemoObj;importnet.csdn.blog.chaijunkun.util.JSONUtil;

importorg.apache.log4j.Logger;

publicclassJSONTest{privatestaticLoggerlogger=Logger.getLogger(JSONTest.class);privatestaticStringjson="{\"sid\":2,\"stuName\":\"\u6C5F\u5357Style\",\"sex\":true,\"birthday\":\"2012-07-15\",\"logTime\":\"2012-12-0419:22:36\"}";publicstaticvoidmain(String[]args){DemoObjobjSrc=newDemoObj();objSrc.setSid(1);objSrc.setStuName("鸟叔");objSrc.setSex(true);Calendarcalendar=Calendar.getInstance();calendar.set(1977,Calendar.DECEMBER,31,0,0,0);objSrc.setBirthday(calendar.getTime());objSrc.setLogTime(newDate());logger.info(String.format("转换为JSON后的数据:%s",JSONUtil.toJSON(objSrc)));DemoObjobjDes=JSONUtil.fromJSON(json,DemoObj.class);if(objDes==null){logger.info("反序列化失败");}else{logger.info("反序列化成功");SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-ddHH: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-0419:31:57"}反序列化成功标识:2姓名:江南Style性别:男生日:2012-07-1500:00:00登录日期:2012-12-0419:22:36

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

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

复制代码代码如下:

importjava.io.IOException;importjava.text.SimpleDateFormat;importjava.util.Date;

importorg.codehaus.jackson.JsonGenerator;importorg.codehaus.jackson.JsonProcessingException;importorg.codehaus.jackson.map.JsonSerializer;importorg.codehaus.jackson.map.SerializerProvider;

publicclassDateTimeSerializerwww.shanxiwang.netextendsJsonSerializer{

@Overridepublicvoidserialize(Datedate,JsonGeneratorgen,SerializerProviderprovider)throwsIOException,JsonProcessingException{SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss");StringformattedDate=sdf.format(date);gen.writeString(formattedDate);}

}



复制代码代码如下:

importjava.io.IOException;importjava.text.SimpleDateFormat;importjava.util.Calendar;importjava.util.Date;

importorg.codehaus.jackson.JsonParser;importorg.codehaus.jackson.JsonProcessingException;importorg.codehaus.jackson.map.DeserializationContext;importorg.codehaus.jackson.map.JsonDeserializer;

publicclassDateTimeDeserializerextendswww.sm136.comJsonDeserializer{

@OverridepublicDatedeserialize(JsonParserparser,DeserializationContextcontext)throwsIOException,JsonProcessingException{StringdateFormat="yyyy-MM-ddHH:mm:ss";SimpleDateFormatsdf=newSimpleDateFormat(dateFormat);try{StringfieldData=parser.getText();returnsdf.parse(fieldData);}catch(Exceptione){Calendarca=Calendar.getInstance();ca.set(1970,Calendar.JANUARY,1,0,0,0);returnca.getTime();}}}



复制代码代码如下:

importjava.io.IOException;importjava.text.SimpleDateFormat;importjava.util.Date;

importorg.codehaus.jackson.JsonGenerator;importorg.codehaus.jackson.JsonProcessingException;importorg.codehaus.jackson.map.JsonSerializer;importorg.codehaus.jackson.map.SerializerProvider;

publicclassDateSerializerextendsJsonSerializer{

@Overridepublicvoidserialize(Datedate,JsonGeneratorgen,SerializerProviderprovider)throwsIOException,JsonProcessingException{SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-dd");StringformattedDate=sdf.format(date);gen.writeString(formattedDate);}

}



复制代码代码如下:

importjava.io.IOException;importjava.text.SimpleDateFormat;importjava.util.Calendar;importjava.util.Date;

importorg.codehaus.jackson.JsonParser;importorg.codehaus.jackson.JsonProcessingException;importorg.codehaus.jackson.map.DeserializationContext;importorg.codehaus.jackson.map.JsonDeserializer;

publicclassDateDeserializerextendsJsonDeserializer{

@OverridepublicDatedeserialize(JsonParserparser,DeserializationContextcontext)throwsIOException,JsonProcessingException{StringdateFormat="yyyy-MM-dd";SimpleDateFormatsdf=newSimpleDateFormat(dateFormat);try{StringfieldData=parser.getText();returnsdf.parse(fieldData);}catch(Exceptione){Calendarca=Calendar.getInstance();ca.set(1970,Calendar.JANUARY,1,0,0,0);returnca.getTime();}}}

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

补充:

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























献花(0)
+1
(本文系白狐一梦首藏)