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. -------------------------------------------
  • 相关阅读:
    linux系统常用命令
    python文件处理
    Python按行读文件
    向脚本传递参数-shift命令
    shell脚本中一些特殊符号
    标准make变量 MAKE_VERSION, CURDIR
    makefile "=" ":=" "?=" "+="
    静态资源映射
    Spring MVC的常用注解
    SpringMVC项目的快速搭建
  • 原文地址:https://www.cnblogs.com/crawl/p/7701856.html
Copyright © 2011-2022 走看看