zoukankan      html  css  js  c++  java
  • 彻底了解构建 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 将胜出。

    建议大家在实际的开发中根据自己的需求合理选择某一方式。

    -------------------------------------------------- Written by crawl~ Crawling on the way of programming. -------------------------------------------
  • 相关阅读:
    1105 Spiral Matrix (25分)(蛇形填数)
    1104 Sum of Number Segments (20分)(long double)
    1026 Table Tennis (30分)(模拟)
    1091 Acute Stroke (30分)(bfs,连通块个数统计)
    1095 Cars on Campus (30分)(排序)
    1098 Insertion or Heap Sort (25分)(堆排序和插入排序)
    堆以及堆排序详解
    1089 Insert or Merge (25分)
    1088 Rational Arithmetic (20分)(模拟)
    1086 Tree Traversals Again (25分)(树的重构与遍历)
  • 原文地址:https://www.cnblogs.com/crawl/p/7701856.html
Copyright © 2011-2022 走看看