徹底了解構建 JSON 字符串的三種方式


原創播客,如需轉載請注明出處。原文地址:http://www.cnblogs.com/crawl/p/7701856.html 

前言:JSON 是輕量級的數據交換格式,很常用,尤其是在使用 Ajax 時,在后台將數據封裝為 JSON 字符串更是常見。之前在做項目的時候用過幾種方式在后端將數組或 List 集合轉換為 JSON 字符串,現在回想起來竟然又有些遺忘。現在來一個匯總,把這幾種構建 JSON 字符串的方式徹底回憶起來。

----------------------------------------------------------------------------------------------------------------------------------------------------------

筆記中提供了大量的代碼示例,需要說明的是,大部分代碼示例都是本人所敲代碼並進行測試,不足之處,請大家指正~

若有疑惑或者需要本系列分享中的資料工具,敬請聯系 qingqing_crawl@163.com

-----------------------------------------------------------------------------------------------------------------------------------------------------------

一、alibaba 的 Fastjson

1.Fastjson 是一個以 Java 語言編寫的 JSON 處理器,由阿里巴巴公司開發,功能強大。

要使用第三方的工具當然要導入 jar 包了,只需導入 fastjson-1.2.8.jar 即可,jar 包的獲取,大家可以直接去網上下載 ,也可以聯系本人。

先來一個 fastjson 的簡單實例吧,如下代碼構造了一個 Customer 的實例,並將此實例轉化成為 JSON 字符串,調用了 com.alibaba.fastjson.JSONtoJSONString() 方法,將 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);
}

打印結果:{"address":"BeiJing","custName":"Tom","id":1}

再來一個小測試,將一個 List 的 Customer 的集合轉換為 JSON 字符串,22 行還是直接調用 JSON 的 toJSONString() 方法,將 List 集合傳入即可

 1 /**
 2 * 將 List 集合轉換為 JSON 字符串
 3 */
 4 @Test
 5 public void test2() {
 6    List<Customer> lists = new ArrayList<>();
 7         
 8    Customer customer = new Customer();
 9    customer.setId(1);
10    customer.setCustName("Tom");
11    customer.setAddress("BeiJing");
12         
13    lists.add(customer);
14         
15    Customer customer2 = new Customer();
16    customer2.setId(1);
17    customer2.setCustName("Bob");
18    customer2.setAddress("ShangHai");
19         
20    lists.add(customer2);
21         
22    String jsonStr = JSON.toJSONString(lists);
23    System.out.println(jsonStr);
24 }

打印結果:[{"address":"BeiJing","custName":"Tom","id":1},{"address":"ShangHai","custName":"Bob","id":1}]

2. 深入研究一下,我們看下面這種情況:3 行創建了一個 List 的 Customer 集合,10 和 11 行進行了一個重復的 add 操作,那么打印結果是什么樣的呢?

 1 @Test
 2 public void test3() {
 3     List<Customer> lists = new ArrayList<>();
 4 
 5     Customer customer = new Customer();
 6     customer.setId(1);
 7     customer.setCustName("Tom");
 8     customer.setAddress("BeiJing");
 9 
10     lists.add(customer);
11     lists.add(customer);
12         
13     String jsonStr = JSON.toJSONString(lists);
14     System.out.println(jsonStr);
15 
16 }

打印結果:[{"address":"BeiJing","custName":"Tom","id":1},{"$ref":"$[0]"}],大家看,第二個 Customer 實例沒有打印出,這就證明了 fastjson 默認禁止循環的引用,如果想改變這種情況,需要在 JSON 的 toJSONString() 方法中傳遞第二個參數 SerializerFeature.DisableCircularReferenceDetect 即可解決,如下:

 1 @Test
 2 public void test3() {
 3   List<Customer> lists = new ArrayList<>();
 4 
 5   Customer customer = new Customer();
 6   customer.setId(1);
 7   customer.setCustName("Tom");
 8   customer.setAddress("BeiJing");
 9 
10   lists.add(customer);
11   lists.add(customer);
12         
13   String jsonStr = JSON.toJSONString(lists, SerializerFeature.DisableCircularReferenceDetect);
14   System.out.println(jsonStr);
15 
16 }

此時的打印結果為:[{"address":"BeiJing","custName":"Tom","id":1},{"address":"BeiJing","custName":"Tom","id":1}],建議以后再使用 JSON 的 toJSONString() 方法時將第二個參數添加上

3.再深入一點,來看一個常見的問題,Department 和 Manager 類維護雙向一對一的關聯關系,Department 類中有 Manager 類的引用,Manager 類中有 Department 類的引用,來看如下代碼:在 11 和 12 行設置了關聯關系,14 行和 15 行進行 JSON 字符串的轉換,結果會怎樣呢?

 1   @Test
 2     public void test4() {
 3         Manager mgr = new Manager();
 4         mgr.setMgrId(1);
 5         mgr.setMgrName("Tom");
 6         
 7         Department dept = new Department();
 8         dept.setDeptId(2);
 9         dept.setDeptName("DEV");
10         
11         mgr.setDept(dept);
12         dept.setManager(mgr);
13         
14         String jsonStr = JSON.toJSONString(dept, SerializerFeature.DisableCircularReferenceDetect);
15 //      String jsonStr = JSON.toJSONString(mgr, SerializerFeature.DisableCircularReferenceDetect);
16         System.out.println(jsonStr);
17     }

答案是,拋出了異常,常見的 java.lang.StackOverflowError,拋異常的原因是雙方都維護關聯關系進入了死循環,那么如何解決這個問題呢?可以在一方添加 @JSONField(serialize=false) 注解,7 行所示,即可解決

 1 public class Department {
 2     
 3     private Integer deptId;
 4     
 5     private String deptName;
 6     
 7     @JSONField(serialize=false)
 8     private Manager manager;
 9 
10     public Integer getDeptId() {
11         return deptId;
12     }
13 
14     public void setDeptId(Integer deptId) {
15         this.deptId = deptId;
16     }
17 
18     public String getDeptName() {
19         return deptName;
20     }
21 
22     public void setDeptName(String deptName) {
23         this.deptName = deptName;
24     }
25 
26     public Manager getManager() {
27         return manager;
28     }
29 
30     public void setManager(Manager manager) {
31         this.manager = manager;
32     }
33     
34 }

打印結果為:{"dept":{"deptId":2,"deptName":"DEV"},"mgrId":1,"mgrName":"Tom"},結果也很令人滿意。

4.最后提供一個 fastjson 的工具類,開發時可以直接使用,供大家參考

  1 package qi.ssh.utils;
  2 
  3 import java.io.IOException;
  4 import java.util.Date;
  5 import java.util.HashMap;
  6 import java.util.Map;
  7 
  8 import javax.servlet.http.HttpServletResponse;
  9 
 10 import com.alibaba.fastjson.JSON;
 11 import com.alibaba.fastjson.serializer.SerializerFeature;
 12 
 13 public class FastJsonUtil {
 14     
 15     /**
 16      * 將對象轉成json串
 17      * @param object
 18      * @return
 19      */
 20     public static String toJSONString(Object object){
 21         //DisableCircularReferenceDetect來禁止循環引用檢測
 22         return JSON.toJSONString(object,SerializerFeature.DisableCircularReferenceDetect);
 23     }
 24     
 25     //輸出json
 26     public static void write_json(HttpServletResponse response,String jsonString)
 27     {
 28         response.setContentType("application/json;utf-8");
 29         response.setCharacterEncoding("UTF-8");
 30         try {
 31             response.getWriter().print(jsonString);
 32         } catch (IOException e) {
 33             // TODO Auto-generated catch block
 34             e.printStackTrace();
 35         }    
 36     }
 37     /**
 38      * ajax提交后回調的json字符串
 39      * @return
 40      */
 41     public static String ajaxResult(boolean success,String message)
 42     {
 43         Map map=new HashMap();
 44         map.put("success", success);//是否成功
 45         map.put("message", message);//文本消息
 46         String json= JSON.toJSONString(map);        
 47         return json;
 48     }
 49     
 50 
 51     /**
 52      * JSON串自動加前綴
 53      * @param json 原json字符串
 54      * @param prefix 前綴
 55      * @return 加前綴后的字符串
 56      */
 57 
 58     public static String JsonFormatterAddPrefix(String json,String prefix,Map<String,Object> newmap)
 59     {
 60         if(newmap == null){
 61             newmap = new HashMap();
 62         }
 63         Map<String,Object> map = (Map) JSON.parse(json);
 64 
 65         for(String key:map.keySet())
 66         {
 67             Object object=map.get(key);
 68             if(isEntity(object)){
 69                 String jsonString = JSON.toJSONString(object);
 70                 JsonFormatterAddPrefix(jsonString,prefix+key+".",newmap);
 71                 
 72             }else{
 73                 newmap.put(prefix+key, object);
 74             }
 75             
 76         }
 77         return JSON.toJSONString(newmap);        
 78     }
 79     /**
 80      * 判斷某對象是不是實體
 81      * @param object
 82      * @return
 83      */
 84     private static boolean isEntity(Object object)
 85     {
 86         if(object instanceof String  )
 87         {
 88             return false;
 89         }
 90         if(object instanceof Integer  )
 91         {
 92             return false;
 93         }
 94         if(object instanceof Long  )
 95         {
 96             return false;
 97         }
 98         if(object instanceof java.math.BigDecimal  )
 99         {
100             return false;
101         }
102         if(object instanceof Date  )
103         {
104             return false;
105         }
106         if(object instanceof java.util.Collection )
107         {
108             return false;
109         }
110         return true;
111         
112     }
113 }

二、Jackson

1.同樣也需要導入 jar 包,Jackson 導入的 jar 包有三個

具體使用也通過一個小例子說明: 

 1 package com.software.jackson;
 2 
 3 import java.util.Arrays;
 4 import java.util.List;
 5 
 6 import com.fasterxml.jackson.annotation.JsonIgnore;
 7 import com.fasterxml.jackson.core.JsonProcessingException;
 8 import com.fasterxml.jackson.databind.ObjectMapper;
 9 
10 public class Customer {
11 
12     private int id;
13 
14     private String name;
15 
16     public Customer(int id, String name) {
17         super();
18         this.id = id;
19         this.name = name;
20     }
21 
22     public int getId() {
23         return id;
24     }
25 
26     public void setId(int id) {
27         this.id = id;
28     }
29 
30     public String getName() {
31         return name;
32     }
33 
34     public void setName(String name) {
35         this.name = name;
36     }
37     
38     public String getCity(){
39         return "BeiJing";
40     }
41     
42     @JsonIgnore
43     public String getSchool(){
44         return "School";
45     }
46     
47     public static void main(String[] args) throws JsonProcessingException {
48         //創建ObjectMapper對象
49         ObjectMapper mapper = new ObjectMapper();
50         
51         Customer customer = new Customer(1, "Tom");
52         List<Customer> lists = Arrays.asList(customer, new Customer(2, "Bob"));
53         
54         //調用 ObjectMapper 的 writeValueAsString(xxx) 方法,把一個對象或幾個傳入,轉為一個 JSON 字符串
55         String jsonStr = mapper.writeValueAsString(lists);
56         System.out.println(jsonStr);
57         
58     }
59 
60 }

 定義了一個 Customer 類,38 行和 43 行定義了兩個額外的 get 方法並直接賦值,main 方法中創建 ObjectMapper 的對象,調用其 writeValueAsString() 方法,傳入單個對象或對象的集合,便會返回對應的 JSON 字符串,打印結果為:[{"id":1,"name":"Tom","city":"BeiJing"},{"id":2,"name":"Bob","city":"BeiJing"}],大家可能會發現,我們 43 行定義的 getSchool() 方法中的 School 沒有被打印出,這是因為我們在此方法上添加了 @JsonIgnore 注解,添加了此注解,在構造 JSON 字符串時便忽略此屬性,細想一下 ,此注解添加到 get 方法上,這也說明 Jackson 構造 JSON 字符串時基於 getter 方法的。

2.與之前一樣,我們想看一看 Jackson 有沒有禁止循環的引用,類似的代碼:

 1   @Test
 2     public void test2() throws JsonProcessingException {
 3         List<Customer> lists = new ArrayList<>();
 4 
 5         Customer customer = new Customer();
 6         customer.setId(1);
 7         customer.setCustName("Tom");
 8         customer.setAddress("BeiJing");
 9 
10         lists.add(customer);
11         lists.add(customer);
12         
13         ObjectMapper mapper = new ObjectMapper();
14         String jsonStr = mapper.writeValueAsString(lists);
15         System.out.println(jsonStr);
16     }

來看一下輸出結果:[{"id":1,"custName":"Tom","address":"BeiJing"},{"id":1,"custName":"Tom","address":"BeiJing"}],結果顯而易見。

3.我們再來看一看如果像 Fastjson 中測試的 Department 和 Manager 雙向一對一映射的例子,Jackson 會表現的怎么樣:

 1   @Test
 2     public void test1() throws JsonProcessingException {
 3         Manager mgr = new Manager();
 4         mgr.setMgrId(1);
 5         mgr.setMgrName("Tom");
 6         
 7         Department dept = new Department();
 8         dept.setDeptId(2);
 9         dept.setDeptName("DEV");
10         
11         mgr.setDept(dept);
12         dept.setManager(mgr);
13         
14         ObjectMapper mapper = new ObjectMapper();
15         String jsonStr = mapper.writeValueAsString(dept);
16         System.out.println(jsonStr);
17     }

直接運行還是會出相同的異常 Caused by: java.lang.StackOverflowError,我們的思路與測試 Fastjson 一樣,為 Department 中的 Manager 引用添加 @JsonIgnore 注解,異常解決了,但打印結果是很滿意,結果為:{"deptId":2,"deptName":"DEV"} ,遠不如 Fastjson 的輸出結果。由此可以看出 Fastjson 功能之強大。

三、Google Gson

1.看看如何使用:jar 包呢只需要一個 gson-2.2.4.jar ,普通對象與集合轉為 JSON 沒有什么可說的,簡單演示一下將 List 集合轉為 JSON 字符串吧,直接 new 出 Gson 的對象,調用其 toJson() 方法傳入需要轉換的對象即可。

 1   @Test
 2     public void test2() {
 3         List<Customer> lists = new ArrayList<>();
 4 
 5         Customer customer = new Customer();
 6         customer.setId(1);
 7         customer.setCustName("Tom");
 8         customer.setAddress("BeiJing");
 9 
10         lists.add(customer);
11 
12         Customer customer2 = new Customer();
13         customer2.setId(1);
14         customer2.setCustName("Bob");
15         customer2.setAddress("ShangHai");
16 
17         lists.add(customer2);
18 
19         Gson gson = new Gson();
20         String jsonStr = gson.toJson(lists);
21         System.out.println(jsonStr);
22     }

打印結果:[{"address":"BeiJing","custName":"Tom","id":1},{"address":"ShangHai","custName":"Bob","id":1}]

2. 那有沒有禁止循環引用呢?

 1   @Test
 2     public void test3() {
 3         List<Customer> lists = new ArrayList<>();
 4 
 5         Customer customer = new Customer();
 6         customer.setId(1);
 7         customer.setCustName("Tom");
 8         customer.setAddress("BeiJing");
 9 
10         lists.add(customer);
11         lists.add(customer);
12         
13         Gson gson = new Gson();
14         String jsonStr = gson.toJson(lists);
15         System.out.println(jsonStr);
16 
17     }

輸出結果:[{"id":1,"custName":"Tom","address":"BeiJing"},{"id":1,"custName":"Tom","address":"BeiJing"}],顯而易見是沒有的。

3.若有雙向一對一的關聯關系映射的話,Google Gson 也是會有死循環問題造成 java.lang.StackOverflowError 異常,但是 Gson 並沒有為我們提供一個注解,要解決此問題樓主提供一個解決方案的思路,Google Gson 使用的是 ExclusionStrategy 策略進行某個字段或某個域的序列化,可以通過此接口自定義一個 注解來解決此問題。但是建議大家如果涉及到雙向關聯關系的對象轉換為  JSON 的需求是,使用 Fastjson。

四、三種方式的簡單比較

樓主從以下幾個方面來比較構造 JSON 字符串的三種方式:

1. jar 包方面:顯然是 Fastjson 和 Google Gson 勝出,Jackson 需要加入 3 個 jar 包。

2. 簡單對象或集合轉為 JSON:若是普通的簡單對象或集合進行轉換,可能 Jackson 和 Google Gson 要勝出一些了,起碼構造比較方便。

3. 涉及到雙向關聯關系的轉換:毫無疑問阿里巴巴的 Fastjson 將勝出。

建議大家在實際的開發中根據自己的需求合理選擇某一方式。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM