深入剖析構(gòu)建JSON字符串的三種方式(推薦)
前言:JSON 是輕量級的數(shù)據(jù)交換格式,很常用,尤其是在使用 Ajax 時,在后臺將數(shù)據(jù)封裝為 JSON 字符串更是常見。之前在做項目的時候用過幾種方式在后端將數(shù)組或 List 集合轉(zhuǎn)換為 JSON 字符串,現(xiàn)在回想起來竟然又有些遺忘?,F(xiàn)在來一個匯總,把這幾種構(gòu)建 JSON 字符串的方式徹底回憶起來。
筆記中提供了大量的代碼示例,需要說明的是,大部分代碼示例都是本人所敲代碼并進(jìn)行測試,不足之處,請大家指正~
一、alibaba 的 Fastjson
1.Fastjson 是一個以 Java 語言編寫的 JSON 處理器,由阿里巴巴公司開發(fā),功能強(qiáng)大。
要使用第三方的工具當(dāng)然要導(dǎo)入 jar 包了,只需導(dǎo)入 fastjson-1.2.8.jar 即可,jar 包的獲取,大家可以直接去網(wǎng)上下載 ,也可以聯(lián)系本人。
先來一個 fastjson 的簡單實例吧,如下代碼構(gòu)造了一個 Customer 的實例,并將此實例轉(zhuǎn)化成為 JSON 字符串,調(diào)用了 com.alibaba.fastjson.JSON 的 toJSONString() 方法,將 Customer 實例傳入
@Test public void test1() { Customer customer = new Customer(); customer.setId(1); customer.setCustName("Tom"); customer.setAddress("BeiJing"); String jsonStr = JSON.toJSONString(customer); System.out.println(jsonStr); }
打印結(jié)果:{"address":"BeiJing","custName":"Tom","id":1}
再來一個小測試,將一個 List 的 Customer 的集合轉(zhuǎn)換為 JSON 字符串,22 行還是直接調(diào)用 JSON 的 toJSONString() 方法,將 List 集合傳入即可
/** * 將 List 集合轉(zhuǎn)換為 JSON 字符串 */ @Test public void test2() { List<Customer> lists = new ArrayList<>(); Customer customer = new Customer(); customer.setId(1); customer.setCustName("Tom"); customer.setAddress("BeiJing"); lists.add(customer); Customer customer2 = new Customer(); customer2.setId(1); customer2.setCustName("Bob"); customer2.setAddress("ShangHai"); lists.add(customer2); String jsonStr = JSON.toJSONString(lists); System.out.println(jsonStr); }
打印結(jié)果:[{"address":"BeiJing","custName":"Tom","id":1},{"address":"ShangHai","custName":"Bob","id":1}]
2. 深入研究一下,我們看下面這種情況:3 行創(chuàng)建了一個 List 的 Customer 集合,10 和 11 行進(jìn)行了一個重復(fù)的 add 操作,那么打印結(jié)果是什么樣的呢?
@Test public void test3() { List<Customer> lists = new ArrayList<>(); Customer customer = new Customer(); customer.setId(1); customer.setCustName("Tom"); customer.setAddress("BeiJing"); lists.add(customer); lists.add(customer); String jsonStr = JSON.toJSONString(lists); System.out.println(jsonStr); }
打印結(jié)果:[{"address":"BeiJing","custName":"Tom","id":1},{"$ref":"$[0]"}],大家看,第二個 Customer 實例沒有打印出,這就證明了 fastjson 默認(rèn)禁止循環(huán)的引用,如果想改變這種情況,需要在 JSON 的 toJSONString() 方法中傳遞第二個參數(shù) SerializerFeature.DisableCircularReferenceDetect 即可解決,如下:
@Test public void test3() { List<Customer> lists = new ArrayList<>(); Customer customer = new Customer(); customer.setId(1); customer.setCustName("Tom"); customer.setAddress("BeiJing"); lists.add(customer); lists.add(customer); String jsonStr = JSON.toJSONString(lists, SerializerFeature.DisableCircularReferenceDetect); System.out.println(jsonStr); }
此時的打印結(jié)果為:[{"address":"BeiJing","custName":"Tom","id":1},{"address":"BeiJing","custName":"Tom","id":1}],建議以后再使用 JSON 的 toJSONString() 方法時將第二個參數(shù)添加上
3.再深入一點,來看一個常見的問題,Department 和 Manager 類維護(hù)雙向一對一的關(guān)聯(lián)關(guān)系,Department 類中有 Manager 類的引用,Manager 類中有 Department 類的引用,來看如下代碼:在 11 和 12 行設(shè)置了關(guān)聯(lián)關(guān)系,14 行和 15 行進(jìn)行 JSON 字符串的轉(zhuǎn)換,結(jié)果會怎樣呢?
@Test public void test4() { Manager mgr = new Manager(); mgr.setMgrId(1); mgr.setMgrName("Tom"); Department dept = new Department(); dept.setDeptId(2); dept.setDeptName("DEV"); mgr.setDept(dept); dept.setManager(mgr); String jsonStr = JSON.toJSONString(dept, SerializerFeature.DisableCircularReferenceDetect); // String jsonStr = JSON.toJSONString(mgr, SerializerFeature.DisableCircularReferenceDetect); System.out.println(jsonStr); }
答案是,拋出了異常,常見的 java.lang.StackOverflowError,拋異常的原因是雙方都維護(hù)關(guān)聯(lián)關(guān)系進(jìn)入了死循環(huán),那么如何解決這個問題呢?可以在一方添加 @JSONField(serialize=false) 注解,7 行所示,即可解決
public class Department { private Integer deptId; private String deptName; @JSONField(serialize=false) private Manager manager; public Integer getDeptId() { return deptId; } public void setDeptId(Integer deptId) { this.deptId = deptId; } public String getDeptName() { return deptName; } public void setDeptName(String deptName) { this.deptName = deptName; } public Manager getManager() { return manager; } public void setManager(Manager manager) { this.manager = manager; } }
打印結(jié)果為:{"dept":{"deptId":2,"deptName":"DEV"},"mgrId":1,"mgrName":"Tom"},結(jié)果也很令人滿意。
4.最后提供一個 fastjson 的工具類,開發(fā)時可以直接使用,供大家參考
package qi.ssh.utils; import java.io.IOException; import java.util.Date; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletResponse; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; public class FastJsonUtil { /** * 將對象轉(zhuǎn)成json串 * @param object * @return */ public static String toJSONString(Object object){ //DisableCircularReferenceDetect來禁止循環(huán)引用檢測 return JSON.toJSONString(object,SerializerFeature.DisableCircularReferenceDetect); } //輸出json public static void write_json(HttpServletResponse response,String jsonString) { response.setContentType("application/json;utf-8"); response.setCharacterEncoding("UTF-8"); try { response.getWriter().print(jsonString); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * ajax提交后回調(diào)的json字符串 * @return */ public static String ajaxResult(boolean success,String message) { Map map=new HashMap(); map.put("success", success);//是否成功 map.put("message", message);//文本消息 String json= JSON.toJSONString(map); return json; } /** * JSON串自動加前綴 * @param json 原json字符串 * @param prefix 前綴 * @return 加前綴后的字符串 */ public static String JsonFormatterAddPrefix(String json,String prefix,Map<String,Object> newmap) { if(newmap == null){ newmap = new HashMap(); } Map<String,Object> map = (Map) JSON.parse(json); for(String key:map.keySet()) { Object object=map.get(key); if(isEntity(object)){ String jsonString = JSON.toJSONString(object); JsonFormatterAddPrefix(jsonString,prefix+key+".",newmap); }else{ newmap.put(prefix+key, object); } } return JSON.toJSONString(newmap); } /** * 判斷某對象是不是實體 * @param object * @return */ private static boolean isEntity(Object object) { if(object instanceof String ) { return false; } if(object instanceof Integer ) { return false; } if(object instanceof Long ) { return false; } if(object instanceof java.math.BigDecimal ) { return false; } if(object instanceof Date ) { return false; } if(object instanceof java.util.Collection ) { return false; } return true; } }
二、Jackson
1.同樣也需要導(dǎo)入 jar 包,Jackson 導(dǎo)入的 jar 包有三個
具體使用也通過一個小例子說明:
package com.software.jackson; import java.util.Arrays; import java.util.List; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; public class Customer { private int id; private String name; public Customer(int id, String name) { super(); this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCity(){ return "BeiJing"; } @JsonIgnore public String getSchool(){ return "School"; } public static void main(String[] args) throws JsonProcessingException { //創(chuàng)建ObjectMapper對象 ObjectMapper mapper = new ObjectMapper(); Customer customer = new Customer(1, "Tom"); List<Customer> lists = Arrays.asList(customer, new Customer(2, "Bob")); //調(diào)用 ObjectMapper 的 writeValueAsString(xxx) 方法,把一個對象或幾個傳入,轉(zhuǎn)為一個 JSON 字符串 String jsonStr = mapper.writeValueAsString(lists); System.out.println(jsonStr); } }
定義了一個 Customer 類,38 行和 43 行定義了兩個額外的 get 方法并直接賦值,main 方法中創(chuàng)建 ObjectMapper 的對象,調(diào)用其 writeValueAsString() 方法,傳入單個對象或?qū)ο蟮募?,便會返回對?yīng)的 JSON 字符串,打印結(jié)果為:[{"id":1,"name":"Tom","city":"BeiJing"},{"id":2,"name":"Bob","city":"BeiJing"}],大家可能會發(fā)現(xiàn),我們 43 行定義的 getSchool() 方法中的 School 沒有被打印出,這是因為我們在此方法上添加了 @JsonIgnore 注解,添加了此注解,在構(gòu)造 JSON 字符串時便忽略此屬性,細(xì)想一下 ,此注解添加到 get 方法上,這也說明 Jackson 構(gòu)造 JSON 字符串時基于 getter 方法的。
2.與之前一樣,我們想看一看 Jackson 有沒有禁止循環(huán)的引用,類似的代碼:
@Test public void test2() throws JsonProcessingException { List<Customer> lists = new ArrayList<>(); Customer customer = new Customer(); customer.setId(1); customer.setCustName("Tom"); customer.setAddress("BeiJing"); lists.add(customer); lists.add(customer); ObjectMapper mapper = new ObjectMapper(); String jsonStr = mapper.writeValueAsString(lists); System.out.println(jsonStr); }
來看一下輸出結(jié)果:[{"id":1,"custName":"Tom","address":"BeiJing"},{"id":1,"custName":"Tom","address":"BeiJing"}],結(jié)果顯而易見。
3.我們再來看一看如果像 Fastjson 中測試的 Department 和 Manager 雙向一對一映射的例子,Jackson 會表現(xiàn)的怎么樣:
@Test public void test1() throws JsonProcessingException { Manager mgr = new Manager(); mgr.setMgrId(1); mgr.setMgrName("Tom"); Department dept = new Department(); dept.setDeptId(2); dept.setDeptName("DEV"); mgr.setDept(dept); dept.setManager(mgr); ObjectMapper mapper = new ObjectMapper(); String jsonStr = mapper.writeValueAsString(dept); System.out.println(jsonStr); }
直接運行還是會出相同的異常 Caused by: java.lang.StackOverflowError,我們的思路與測試 Fastjson 一樣,為 Department 中的 Manager 引用添加 @JsonIgnore 注解,異常解決了,但打印結(jié)果是很滿意,結(jié)果為:{"deptId":2,"deptName":"DEV"} ,遠(yuǎn)不如 Fastjson 的輸出結(jié)果。由此可以看出 Fastjson 功能之強(qiáng)大。
三、Google Gson
1.看看如何使用:jar 包呢只需要一個 gson-2.2.4.jar ,普通對象與集合轉(zhuǎn)為 JSON 沒有什么可說的,簡單演示一下將 List 集合轉(zhuǎn)為 JSON 字符串吧,直接 new 出 Gson 的對象,調(diào)用其 toJson() 方法傳入需要轉(zhuǎn)換的對象即可。
@Test public void test2() { List<Customer> lists = new ArrayList<>(); Customer customer = new Customer(); customer.setId(1); customer.setCustName("Tom"); customer.setAddress("BeiJing"); lists.add(customer); Customer customer2 = new Customer(); customer2.setId(1); customer2.setCustName("Bob"); customer2.setAddress("ShangHai"); lists.add(customer2); Gson gson = new Gson(); String jsonStr = gson.toJson(lists); System.out.println(jsonStr); }
打印結(jié)果:[{"address":"BeiJing","custName":"Tom","id":1},{"address":"ShangHai","custName":"Bob","id":1}]
2. 那有沒有禁止循環(huán)引用呢?
@Test public void test3() { List<Customer> lists = new ArrayList<>(); Customer customer = new Customer(); customer.setId(1); customer.setCustName("Tom"); customer.setAddress("BeiJing"); lists.add(customer); lists.add(customer); Gson gson = new Gson(); String jsonStr = gson.toJson(lists); System.out.println(jsonStr); }
輸出結(jié)果:[{"id":1,"custName":"Tom","address":"BeiJing"},{"id":1,"custName":"Tom","address":"BeiJing"}],顯而易見是沒有的。
3.若有雙向一對一的關(guān)聯(lián)關(guān)系映射的話,Google Gson 也是會有死循環(huán)問題造成 java.lang.StackOverflowError 異常,但是 Gson 并沒有為我們提供一個注解,要解決此問題LZ提供一個解決方案的思路,Google Gson 使用的是 ExclusionStrategy 策略進(jìn)行某個字段或某個域的序列化,可以通過此接口自定義一個 注解來解決此問題。但是建議大家如果涉及到雙向關(guān)聯(lián)關(guān)系的對象轉(zhuǎn)換為 JSON 的需求是,使用 Fastjson。
四、三種方式的簡單比較
LZ 從以下幾個方面來比較構(gòu)造 JSON 字符串的三種方式:
1. jar 包方面:顯然是 Fastjson 和 Google Gson 勝出,Jackson 需要加入 3 個 jar 包。
2. 簡單對象或集合轉(zhuǎn)為 JSON:若是普通的簡單對象或集合進(jìn)行轉(zhuǎn)換,可能 Jackson 和 Google Gson 要勝出一些了,起碼構(gòu)造比較方便。
3. 涉及到雙向關(guān)聯(lián)關(guān)系的轉(zhuǎn)換:毫無疑問阿里巴巴的 Fastjson 將勝出。
建議大家在實際的開發(fā)中根據(jù)自己的需求合理選擇某一方式。
以上這篇深入剖析構(gòu)建JSON字符串的三種方式(推薦)就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Mybatis Plus 自定義方法實現(xiàn)分頁功能的示例代碼
這篇文章主要介紹了Mybatis Plus 自定義方法實現(xiàn)分頁功能的示例代碼,代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08SpringApplicationRunListener監(jiān)聽器源碼詳解
這篇文章主要介紹了SpringApplicationRunListener監(jiān)聽器源碼詳解,springboot提供了兩個類SpringApplicationRunListeners、SpringApplicationRunListener(EventPublishingRunListener),spring框架還提供了一個ApplicationListener接口,需要的朋友可以參考下2023-11-11Java中的volatile實現(xiàn)機(jī)制詳細(xì)解析
這篇文章主要介紹了Java中的volatile實現(xiàn)機(jī)制詳細(xì)解析,本文的主要內(nèi)容就在于要理解volatile的緩存的一致性協(xié)議導(dǎo)致的共享變量可見性,以及volatile在解析成為匯編語言的時候?qū)ψ兞考渔i兩塊理論內(nèi)容,需要的朋友可以參考下2024-01-01