我們都知道,Jackson JSON以高速、方便和靈活著稱。之前的文章中介紹過使用注解的形式來規(guī)定如何將一個(gè)對(duì)象序列化成JSON的方法,以及如何將一個(gè)JSON數(shù)據(jù)反序列化到一個(gè)對(duì)象上。但是美中不足的一點(diǎn)就是對(duì)于中文的處理。當(dāng)然我說的美中不足是在默認(rèn)情況下,Jackson JSON不會(huì)將中文等非ASCII字符轉(zhuǎn)換為/uFFFF這樣的形式來顯示。也就是說默認(rèn)情況下會(huì)顯示為{"name":"張三"}而不是{"name":"/u5F20/u4E09"}。那么為什么有這樣的需求呢?在HTTP協(xié)議中,我們可以指定數(shù)據(jù)頭部分的內(nèi)容編碼。如:“GBK”、“UTF-8”等等。如果你設(shè)置正確了,那么OK,前者所表示的數(shù)據(jù)您可以正確處理。然而如果設(shè)置錯(cuò)誤,對(duì)于中文字符將會(huì)產(chǎn)生亂碼。兩套應(yīng)用系統(tǒng)對(duì)接,有可能兩邊使用的默認(rèn)編碼不同,如果一方修改默認(rèn)編碼將會(huì)對(duì)應(yīng)用造成不可預(yù)知的后果。因此若能以長(zhǎng)遠(yuǎn)的眼光開發(fā),那么無論您設(shè)置成什么編碼方式,都不會(huì)使數(shù)據(jù)產(chǎn)生亂碼。因?yàn)椋@里用到了萬國(guó)編碼――Unicode。
好的,問題出來了,我們?nèi)绾谓鉀Q呢?使其通過實(shí)驗(yàn),Jackson JSON其實(shí)在默認(rèn)設(shè)置下已經(jīng)具備了對(duì)Unicode編碼的JSON數(shù)據(jù)進(jìn)行解析。所欠缺的就是在序列化對(duì)象時(shí)缺少相應(yīng)的步驟。好在Jackson JSON框架允許我們自定義序列化方法。那么我們就來寫一個(gè)序列化類:
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中字符串的開頭引號(hào)
for (char c : str.toCharArray()) {
if (c >= 0x80){
writeUnicodeEscape(gen, c); // 為所有非ASCII字符生成轉(zhuǎn)義的unicode字符
}else {
// 為ASCII字符中前128個(gè)字符使用轉(zhuǎn)義的unicode字符
int code = (c < ESCAPE_CODES.length ? ESCAPE_CODES[c] : 0);
if (code == 0){
gen.writeRaw(c); // 此處不用轉(zhuǎn)義
}else if (code < 0){
writeUnicodeEscape(gen, (char) (-code - 1)); // 通用轉(zhuǎn)義字符
}else {
writeShortEscape(gen, (char) code); // 短轉(zhuǎn)義字符 (/n /t ...)
}
}
}
gen.writeRaw('"');//寫入JSON中字符串的結(jié)束引號(hào)
}
}
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
}
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("轉(zhuǎn)換為JSON后的數(shù)據(jù):%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("標(biāo)識(shí):%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())));
}
}
}
細(xì)心的朋友也許觀察到了,在測(cè)試用的對(duì)象定義代碼中,針對(duì)同樣Date類型的屬性“birthday”和“l(fā)ogTime”,我們指定了不同的序列化與反序列化方法。讓我們來看爛這兩個(gè)有什么不同:
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 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 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 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();
}
}
}
補(bǔ)充:
最近有一個(gè)需求,需要在序列化與反序列化對(duì)象的時(shí)候?qū)?shù)據(jù)進(jìn)行修改,當(dāng)發(fā)現(xiàn)數(shù)據(jù)源值為空時(shí)需要讓生成的JSON顯示改字段為“游客”。可是我無論如何指定序列化器與反序列化器都無效。程序根本走不到指定的代碼中去。后來我得出結(jié)論,Jackson JSON在反序列化對(duì)象的時(shí)候,若JSON數(shù)據(jù)中對(duì)應(yīng)屬性為null,則不會(huì)走自定義的反序列化器;同樣地,當(dāng)你設(shè)置對(duì)象的某個(gè)屬性值為null時(shí),在將其序列化成JSON時(shí),也不會(huì)走自定義的序列化器。因此若有類似的需求,請(qǐng)?jiān)谛蛄谢c反序列化之前通過硬代碼形式判斷和修改,千萬不要什么事都指望著序列化器與反序列化器。
新聞熱點(diǎn)
疑難解答
圖片精選