深入剖析构建JSON字符串的三种方式(推荐)

前言:JSON 是轻量级的数据交换格式,很常用,尤其是在使用 Ajax 时,在后台将数据封装为 JSON 字符串更是常见。之前在做项目的时候用过几种方式在后端将数组或 List 集合转换为 JSON 字符串,现在回想起来竟然又有些遗忘。现在来一个汇总,把这几种构建 JSON 字符串的方式彻底回忆起来。

笔记中提供了大量的代码示例,需要说明的是,大部分代码示例都是本人所敲代码并进行测试,不足之处,请大家指正~

一、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 即可解决,如下:

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

}

此时的打印结果为:[{"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 并没有为我们提供一个注解,要解决此问题LZ提供一个解决方案的思路,Google Gson 使用的是 ExclusionStrategy 策略进行某个字段或某个域的序列化,可以通过此接口自定义一个 注解来解决此问题。但是建议大家如果涉及到双向关联关系的对象转换为 JSON 的需求是,使用 Fastjson。

四、三种方式的简单比较

LZ 从以下几个方面来比较构造 JSON 字符串的三种方式:

1. jar 包方面:显然是 Fastjson 和 Google Gson 胜出,Jackson 需要加入 3 个 jar 包。

2. 简单对象或集合转为 JSON:若是普通的简单对象或集合进行转换,可能 Jackson 和 Google Gson 要胜出一些了,起码构造比较方便。

3. 涉及到双向关联关系的转换:毫无疑问阿里巴巴的 Fastjson 将胜出。

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

以上这篇深入剖析构建JSON字符串的三种方式(推荐)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 基于java解析JSON的三种方式详解

    本文实例分析了基于java解析JSON的三种方式.分享给大家供大家参考,具体如下: 一.什么是JSON? JSON是一种取代XML的数据结构,和xml相比,它更小巧但描述能力却不差,由于它的小巧所以网络传输数据将减少更多流量从而加快速度. JSON就是一串字符串 只不过元素会使用特定的符号标注. {} 双括号表示对象 [] 中括号表示数组 "" 双引号内是属性或值 : 冒号表示后者是前者的值(这个值可以是字符串.数字.也可以是另一个数组或对象) 所以 {"name"

  • Json对象与Json字符串互转(4种转换方式)

    1>jQuery插件支持的转换方式: 复制代码 代码如下: $.parseJSON( jsonstr ); //jQuery.parseJSON(jsonstr),可以将json字符串转换成json对象 2>浏览器支持的转换方式(Firefox,chrome,opera,safari,ie9,ie8)等浏览器: 复制代码 代码如下: JSON.parse(jsonstr); //可以将json字符串转换成json对象 JSON.stringify(jsonobj); //可以将json对象转换

  • 详解java生成json字符串的方法

    例1:将map对象添加一次元素(包括字符串对.数组),转换成json对象一次. 代码: package com.json; //这是使用org.json的程序: import java.util.HashMap; import java.util.Map; import org.json.JSONException; import org.json.JSONObject; public class jsontest { public static void main(String[] args)

  • 深入剖析构建JSON字符串的三种方式(推荐)

    前言:JSON 是轻量级的数据交换格式,很常用,尤其是在使用 Ajax 时,在后台将数据封装为 JSON 字符串更是常见.之前在做项目的时候用过几种方式在后端将数组或 List 集合转换为 JSON 字符串,现在回想起来竟然又有些遗忘.现在来一个汇总,把这几种构建 JSON 字符串的方式彻底回忆起来. 笔记中提供了大量的代码示例,需要说明的是,大部分代码示例都是本人所敲代码并进行测试,不足之处,请大家指正~ 一.alibaba 的 Fastjson 1.Fastjson 是一个以 Java 语言

  • 解析Json字符串的三种方法日常常用

    在很多时候,我们的需要将类似 json 格式的字符串数据转为json, 下面将介绍日常中使用的三种解析json字符串的方法 1.首先,我们先看一下什么是 json 格式字符串数据,很简单,就是 json 字符串化,在json 最外加单/双号变为字符串数据    var str='{"name":"Mike","sex":"女","age":"29"}'; var t2="[{n

  • SpringMVC返回json数据的三种方式

    Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面.Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块.使用 Spring 可插入的 MVC架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的SpringMVC框架或集成其他MVC开发框架,如Struts1,Struts2等. 1.第一种方式是spring2时代的产物,也就是每个json视图controller配置一个Jsoniew. 如:<bean

  • JavaScript常用截取字符串的三种方式用法区别实例解析

    stringObject.substring(start,stop) 用于提取字符串中介于两个指定下标之间的字符. start必需.一个非负的整数,规定要提取的子串的第一个字符在 stringObject 中的位置. stop可选.一个非负的整数,比要提取的子串的最后一个字符在 stringObject 中的位置多 1.如果省略该参数,那么返回的子串会一直到字符串的结尾. start从0开始 到stop(不包含stop)结束 不接受负的参数. stringObject.substr(start,

  • 详解Springboot之接收json字符串的两种方式

    第一种方式.通过关键字段@RequestBody,标明这个对象接收json字符串.还有第二种方式,直接通过request来获取流.在spring中,推荐使用. 代码地址 https://gitee.com/yellowcong/springboot-demo/tree/master/springboot-json 项目结构 其实项目里面没啥类容,就是一个控制器和pom.xml配置 配置fastjson 添加fastjson的依赖到pom.xml中 <dependency> <groupI

  • mvc中form表单提交的三种方式(推荐)

    第一种方式:submit 按钮 提交 <form action="MyDemand" method="post"> <span>关键字:</span> <input name="keywords" type="text" value="@keywords" /> <input type="submit" value="搜索&

  • 前端js文件合并的三种方式推荐

    最近在思考前端js文件该如何合并,当然不包括不能合并文件,而是我们能合并的文件,想了想应该也只有三种方式. 三个方式如下: 1. 一个大文件,所有js合并成一个大文件,所有页面都引用它. 2. 各个页面大文件,各自页面合并生成自己所需js的大文件. 3. 合并多个共用大文件,根据实践情况合并出多个共用js文件,每个页面引用多个共用大文件. 另外在我看来,合并有两个目的: 1. 为了减少请求数. 2. 代码安全考虑(文件分得越多,越容易被人看清). PS:注意我说的不是压缩混淆,只是合并 1. 一

  • scala中停止循环的三种方式(推荐)

    Scala 循环 有的时候,我们可能需要多次执行同一块代码.一般情况下,语句是按顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推. 编程语言提供了更为复杂执行路径的多种控制结构. 循环语句允许我们多次执行一个语句或语句组,下面是大多数编程语言中循环语句的流程图: 开始正文介绍: 1:使用return关键字 object BreakLoop { //1.使用return关键字 def add():Unit= { for(i <- 1 to 10){ if(i==7){ //停止循环

  • Angularjs 自定义服务的三种方式(推荐)

    AngularJS简介: AngularJS 通过新的属性和表达式扩展了 HTML. AngularJS 可以构建一个单一页面应用程序(SPAs:Single Page Applications). AngularJS 学习起来非常简单. angularjs 中可通过三种($provider,$factory,$service)方式自定义服务,以下是不同的实现形式: // 定义module , module中注入$provide var starterApp = angular.module('

  • Spring循环依赖的三种方式(推荐)

    引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错.下面说一下spring是如果解决循环依赖的. 第一种:构造器参数循环依赖 Spring容器会将每一个正在创建的Bean 标识符放在一个"当前创建Bean池"中,Bean标识符在创建过程中将一直保持 在这个池中,因此如果在创建Bean过程中发现自己已经在"当前创建Bean池"里时将抛出 BeanCurrentlyInCrea

随机推荐