在開發的過程中,經常會碰到和自己預期不一樣的情況。有的時候自己去研究一下還是很有趣的。這兩天在寫java web的時候,碰到了一個對象序列化的問題。
問題重現
public class AjaxJson {
private boolean success;
private String msg;
private Object obj;
private Map attributes;
//getter and setter
public String getJsonStr() {
JSONObject obj = new JSONObject();
obj.put("success", this.isSuccess());
obj.put("msg", this.getMsg());
obj.put("obj", this.obj);
obj.put("attributes", this.attributes);
return obj.toJSONString();
}
}
上面是一個接口類,我們需要把這個類的對象序列化成json返回。那么在springmvc中,一般是這樣操作的。
@RequestMapping(params = "/get")
@ResponseBody
public AjaxJson del(HttpServletRequest request) {
AjaxJson json = new AjaxJson();
//省略業務操作
return json;
}
默認的話,返回ResponseBody,對象會直接序列化成json。這個時候,我們可以看一下返回的json。
{
"success": "true",
"Msg":"1",
"obj":{
...
},
"attributes": null,
"jsonStr":"{"success": "true","Msg":"1","obj":{...},"attributes": null,}"
}
顯然,和我們預期想的不太一樣,多了一個jsonstr字段。這個時候我在想,是不是springmvc的問題。結果仔細一想,springnvc之所以可以直接將對象序列化成json,其實是我們添加的配置文件在起作用,真正參與序列化工作的是jackson這個庫。于是,我單獨使用了jackson,結果返回的json字符串和之前是相同的,這下就可以肯定是,jackson這個庫本身的設計問題了。
深入探討
帶著這份好奇,我把java中常用的json序列化的庫都試了一下,看看是否都是這樣。主流的庫有jackson、fastjson和gson。
經過測試發現,jackson和阿里的fastjson返回的json字符串都帶有一個jsonstr字段,唯獨google的gson返回了我們預期的結果——只序列化對象的field。
于是我找了下這幾個庫的序列化原理:
jackson和fastjson
在序列化的時候,先利用反射找到對象類的所有get方法,接下來去get,然后小寫化,作為json的每個key值,而get方法的返回值作為value。接下來再反射field,添加到json中。
gson
沒有找到通俗的講法,不過感覺應該就和getter方法無關吧。
所以,可以看大我們的AjaxJson類中存在這樣一個getJsonStr,因此,jsonStr就作為key,序列化到json中了。
當然在jackson中,提供了相應的annotation,可以把這類方法忽略掉。在方法前加上**@JsonIgnore **即可。
個人理解
遇到問題的時候,千萬不要忽略一些簡單的地方,例如getter和setter方法。用getXXX的地方,可以用fetch等替代。
有時我們會在類中定義例如private int mAge的變量,而getter的方法是getAge()。顯然我們希望在序列化的時候得到的key為age而非mAge,那么反射getter方法也就有它存在的意義了。
參考文獻