原创播客,如需转载请注明出处。原文地址: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.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);
}

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

再来一个小测试,将一个 List 的 Customer 的集合转换为 JSON 字符串,22 行还是直接调用 JSON 的 toJSONString() 方法,将 List 集合传入即可

 /**
* 将 List 集合转换为 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);
}

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

2. 深入研究一下,我们看下面这种情况:3 行创建了一个 List 的 Customer 集合,10 和 11 行进行了一个重复的 add 操作,那么打印结果是什么样的呢?

 @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); }

打印结果:[{"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<>();   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); }

此时的打印结果为:[{"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 字符串的转换,结果会怎样呢?

   @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,抛异常的原因是双方都维护关联关系进入了死循环,那么如何解决这个问题呢?可以在一方添加 @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;
} }

打印结果为:{"dept":{"deptId":2,"deptName":"DEV"},"mgrId":1,"mgrName":"Tom"},结果也很令人满意。

4.最后提供一个 fastjson 的工具类,开发时可以直接使用,供大家参考

 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 { /**
* 将对象转成json串
* @param object
* @return
*/
public static String toJSONString(Object object){
//DisableCircularReferenceDetect来禁止循环引用检测
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提交后回调的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.同样也需要导入 jar 包,Jackson 导入的 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 {
//创建ObjectMapper对象
ObjectMapper mapper = new ObjectMapper(); Customer customer = new Customer(1, "Tom");
List<Customer> lists = Arrays.asList(customer, new Customer(2, "Bob")); //调用 ObjectMapper 的 writeValueAsString(xxx) 方法,把一个对象或几个传入,转为一个 JSON 字符串
String jsonStr = mapper.writeValueAsString(lists);
System.out.println(jsonStr); } }

定义了一个 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 有没有禁止循环的引用,类似的代码:

   @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);
}

来看一下输出结果:[{"id":1,"custName":"Tom","address":"BeiJing"},{"id":1,"custName":"Tom","address":"BeiJing"}],结果显而易见。

3.我们再来看一看如果像 Fastjson 中测试的 Department 和 Manager 双向一对一映射的例子,Jackson 会表现的怎么样:

   @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 注解,异常解决了,但打印结果是很满意,结果为:{"deptId":2,"deptName":"DEV"} ,远不如 Fastjson 的输出结果。由此可以看出 Fastjson 功能之强大。

三、Google Gson

1.看看如何使用:jar 包呢只需要一个 gson-2.2.4.jar ,普通对象与集合转为 JSON 没有什么可说的,简单演示一下将 List 集合转为 JSON 字符串吧,直接 new 出 Gson 的对象,调用其 toJson() 方法传入需要转换的对象即可。

   @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);
}

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

2. 那有没有禁止循环引用呢?

   @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); }

输出结果:[{"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 将胜出。

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

彻底了解构建 JSON 字符串的三种方式的更多相关文章

  1. spring接收json字符串的两种方式

    一.前言 前几天遇到一个问题,前端H5调用我的springboot一个接口(post方式,@RequestParameter接收参数),传入的参数接收不到.自己测试接口时使用postman的form- ...

  2. Solon 开发,三、构建一个Bean的三种方式

    Solon 开发 一.注入或手动获取配置 二.注入或手动获取Bean 三.构建一个Bean的三种方式 四.Bean 扫描的三种方式 五.切面与环绕拦截 六.提取Bean的函数进行定制开发 七.自定义注 ...

  3. 解析Json字符串的三种方法

    在很多时候,我们的需要将类似 json 格式的字符串数据转为json, 下面将介绍日常中使用的三种解析json字符串的方法 1.首先,我们先看一下什么是 json 格式字符串数据,很简单,就是 jso ...

  4. DataTable转换为Json字符串的三种方法

    //第一种:使用StringBuilder  public string DataTableToJson(DataTable table) { var JsonString = new StringB ...

  5. JS中将json字符串转为json对象的三种方式

    第一种:利用JSON的parse方法,即jsonObj=JSON.parse(jsonStr); 第二种:jsonObj = eval('(' + jsonStr+ ')'); 第三种:比较难理解:j ...

  6. SpringMVC返回json数据的三种方式

    1.第一种方式是spring2时代的产物,也就是每个json视图controller配置一个Jsoniew. 如:<bean id="defaultJsonView" cla ...

  7. SpringMVC返回json数据的三种方式(转)

    原文:https://blog.csdn.net/shan9liang/article/details/42181345# 1.第一种方式是spring2时代的产物,也就是每个json视图contro ...

  8. Gson解析复杂JSON字符串的两种方式

    JSON解析可以使用的库: JSONObject(源自Android官方). Gson(源自Google). Jackson(第三方开源库). FastJSON(第三方开源库). 本文例子使用Goog ...

  9. java后台处理解析json字符串的两种方式

    简单说一下背景 上次后端通过模拟http请求百度地图接口,得到的是一个json字符串,而我只需要其中的某个key对应的value. 当时我是通过截取字符串取的,后来觉得不太合理,今天整理出了两种处理解 ...

随机推荐

  1. 201521123118《java程序设计》第一周学习总结

    1. 本周学习总结 根据学习的过程中,虽然听学习过的人说过,c语言和java语言差不多,学习过c语言 在学java会比较容易,但是这一周发现,java和c还是有一些差别的: java语言是面向对象的语 ...

  2. 201521123095 《Java程序设计》第10周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 本次PTA作业题集异常.多线程 Q1.finally 题目4-2 1.1 截图你的提交结果( ...

  3. MarkDown模板

    一个例子: 例子开始 1. 本章学习总结 今天主要学习了三个知识点 封装 继承 多态 2. 书面作业 Q1. java HelloWorld命令中,HelloWorld这个参数是什么含义? 今天学了一 ...

  4. RSA原理、ssl认证、Tomcat中配置数字证书以及网络传输数据中的密码学知识

      情形一:接口的加.解密与加.验签 rsa不是只有加密解密,除此外还有加签和验签.之前一直误以为加密就是加签,解密就是验签.这是错误的! 正确的理解是: 数据传输的机密性:公钥加密私钥解密是密送,保 ...

  5. Git 基本命令有哪些

    Git 相关命令 git init 初始化一个项目 git clone 利用url 从远程clone下来一个项目 git status 查看当前项目修改状态 git log 查看日志 查看历史记录 g ...

  6. [js学习笔记] 原型链理解

    js中只有对象,包括对象,函数,常量等. prototype 只有函数里有这个属性. 对象里可以设置这个属性,但是没这个属性的意义 prototype指向一个对象,可以添加想要的方法. 该对象里有一个 ...

  7. OC——关于KVC

    我们知道在C#中可以通过反射读写一个对象的属性,有时候这种方式特别方便,因为你可以利用字符串的方式去动态控制一个对象.其实由于ObjC的语言特性,你根部不必进行任何操作就可以进行属性的动态读写,这种方 ...

  8. Piggy Back_KEY

    Piggy Back (piggyback.pas/c/cpp) [问题描述] Bessie 和她的姐姐 Elsie 在不同的田块吃草,晚上她们都返回牛棚休息.作为聪明的奶牛,她们想设计一个方案使得步 ...

  9. android studio集成ijkplayer

    介绍 ijkplayer是一款非常火的开源视频播放器,android和IOS通用.关于怎么编译怎么导入android Studio中自己的项目,其中坑很多,本篇记录下自己的操作记录.ijkplayer ...

  10. P1013

    问题 D: P1013 时间限制: 1 Sec  内存限制: 128 MB提交: 33  解决: 21[提交][状态][讨论版] 题目描述 " 找啊找啊找GF,找到一个好GF,吃顿饭啊拉拉手 ...