深入剖析構(gòu)建JSON字符串的三種方式(推薦)
前言:JSON 是輕量級(jí)的數(shù)據(jù)交換格式,很常用,尤其是在使用 Ajax 時(shí),在后臺(tái)將數(shù)據(jù)封裝為 JSON 字符串更是常見。之前在做項(xiàng)目的時(shí)候用過幾種方式在后端將數(shù)組或 List 集合轉(zhuǎn)換為 JSON 字符串,現(xiàn)在回想起來竟然又有些遺忘。現(xiàn)在來一個(gè)匯總,把這幾種構(gòu)建 JSON 字符串的方式徹底回憶起來。
筆記中提供了大量的代碼示例,需要說明的是,大部分代碼示例都是本人所敲代碼并進(jìn)行測(cè)試,不足之處,請(qǐng)大家指正~
一、alibaba 的 Fastjson
1.Fastjson 是一個(gè)以 Java 語言編寫的 JSON 處理器,由阿里巴巴公司開發(fā),功能強(qiáng)大。
要使用第三方的工具當(dāng)然要導(dǎo)入 jar 包了,只需導(dǎo)入 fastjson-1.2.8.jar 即可,jar 包的獲取,大家可以直接去網(wǎng)上下載 ,也可以聯(lián)系本人。
先來一個(gè) fastjson 的簡(jiǎn)單實(shí)例吧,如下代碼構(gòu)造了一個(gè) Customer 的實(shí)例,并將此實(shí)例轉(zhuǎn)化成為 JSON 字符串,調(diào)用了 com.alibaba.fastjson.JSON 的 toJSONString() 方法,將 Customer 實(shí)例傳入
@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);
}
打印結(jié)果:{"address":"BeiJing","custName":"Tom","id":1}
再來一個(gè)小測(cè)試,將一個(gè) List 的 Customer 的集合轉(zhuǎn)換為 JSON 字符串,22 行還是直接調(diào)用 JSON 的 toJSONString() 方法,將 List 集合傳入即可
/**
* 將 List 集合轉(zhuǎn)換為 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);
}
打印結(jié)果:[{"address":"BeiJing","custName":"Tom","id":1},{"address":"ShangHai","custName":"Bob","id":1}]
2. 深入研究一下,我們看下面這種情況:3 行創(chuàng)建了一個(gè) List 的 Customer 集合,10 和 11 行進(jìn)行了一個(gè)重復(fù)的 add 操作,那么打印結(jié)果是什么樣的呢?
@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);
}
打印結(jié)果:[{"address":"BeiJing","custName":"Tom","id":1},{"$ref":"$[0]"}],大家看,第二個(gè) Customer 實(shí)例沒有打印出,這就證明了 fastjson 默認(rèn)禁止循環(huán)的引用,如果想改變這種情況,需要在 JSON 的 toJSONString() 方法中傳遞第二個(gè)參數(shù) 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);
}
此時(shí)的打印結(jié)果為:[{"address":"BeiJing","custName":"Tom","id":1},{"address":"BeiJing","custName":"Tom","id":1}],建議以后再使用 JSON 的 toJSONString() 方法時(shí)將第二個(gè)參數(shù)添加上
3.再深入一點(diǎn),來看一個(gè)常見的問題,Department 和 Manager 類維護(hù)雙向一對(duì)一的關(guān)聯(lián)關(guān)系,Department 類中有 Manager 類的引用,Manager 類中有 Department 類的引用,來看如下代碼:在 11 和 12 行設(shè)置了關(guān)聯(lián)關(guān)系,14 行和 15 行進(jìn)行 JSON 字符串的轉(zhuǎn)換,結(jié)果會(huì)怎樣呢?
@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,拋異常的原因是雙方都維護(hù)關(guān)聯(lián)關(guān)系進(jìn)入了死循環(huán),那么如何解決這個(gè)問題呢?可以在一方添加 @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;
}
}
打印結(jié)果為:{"dept":{"deptId":2,"deptName":"DEV"},"mgrId":1,"mgrName":"Tom"},結(jié)果也很令人滿意。
4.最后提供一個(gè) fastjson 的工具類,開發(fā)時(shí)可以直接使用,供大家參考
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 {
/**
* 將對(duì)象轉(zhuǎn)成json串
* @param object
* @return
*/
public static String toJSONString(Object object){
//DisableCircularReferenceDetect來禁止循環(huán)引用檢測(cè)
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提交后回調(diào)的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串自動(dòng)加前綴
* @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);
}
/**
* 判斷某對(duì)象是不是實(shí)體
* @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.同樣也需要導(dǎo)入 jar 包,Jackson 導(dǎo)入的 jar 包有三個(gè)

具體使用也通過一個(gè)小例子說明:
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 {
//創(chuàng)建ObjectMapper對(duì)象
ObjectMapper mapper = new ObjectMapper();
Customer customer = new Customer(1, "Tom");
List<Customer> lists = Arrays.asList(customer, new Customer(2, "Bob"));
//調(diào)用 ObjectMapper 的 writeValueAsString(xxx) 方法,把一個(gè)對(duì)象或幾個(gè)傳入,轉(zhuǎn)為一個(gè) JSON 字符串
String jsonStr = mapper.writeValueAsString(lists);
System.out.println(jsonStr);
}
}
定義了一個(gè) Customer 類,38 行和 43 行定義了兩個(gè)額外的 get 方法并直接賦值,main 方法中創(chuàng)建 ObjectMapper 的對(duì)象,調(diào)用其 writeValueAsString() 方法,傳入單個(gè)對(duì)象或?qū)ο蟮募?,便?huì)返回對(duì)應(yīng)的 JSON 字符串,打印結(jié)果為:[{"id":1,"name":"Tom","city":"BeiJing"},{"id":2,"name":"Bob","city":"BeiJing"}],大家可能會(huì)發(fā)現(xiàn),我們 43 行定義的 getSchool() 方法中的 School 沒有被打印出,這是因?yàn)槲覀冊(cè)诖朔椒ㄉ咸砑恿?@JsonIgnore 注解,添加了此注解,在構(gòu)造 JSON 字符串時(shí)便忽略此屬性,細(xì)想一下 ,此注解添加到 get 方法上,這也說明 Jackson 構(gòu)造 JSON 字符串時(shí)基于 getter 方法的。
2.與之前一樣,我們想看一看 Jackson 有沒有禁止循環(huán)的引用,類似的代碼:
@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);
}
來看一下輸出結(jié)果:[{"id":1,"custName":"Tom","address":"BeiJing"},{"id":1,"custName":"Tom","address":"BeiJing"}],結(jié)果顯而易見。
3.我們?cè)賮砜匆豢慈绻?Fastjson 中測(cè)試的 Department 和 Manager 雙向一對(duì)一映射的例子,Jackson 會(huì)表現(xiàn)的怎么樣:
@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);
}
直接運(yùn)行還是會(huì)出相同的異常 Caused by: java.lang.StackOverflowError,我們的思路與測(cè)試 Fastjson 一樣,為 Department 中的 Manager 引用添加 @JsonIgnore 注解,異常解決了,但打印結(jié)果是很滿意,結(jié)果為:{"deptId":2,"deptName":"DEV"} ,遠(yuǎn)不如 Fastjson 的輸出結(jié)果。由此可以看出 Fastjson 功能之強(qiáng)大。
三、Google Gson
1.看看如何使用:jar 包呢只需要一個(gè) gson-2.2.4.jar ,普通對(duì)象與集合轉(zhuǎn)為 JSON 沒有什么可說的,簡(jiǎn)單演示一下將 List 集合轉(zhuǎn)為 JSON 字符串吧,直接 new 出 Gson 的對(duì)象,調(diào)用其 toJson() 方法傳入需要轉(zhuǎn)換的對(duì)象即可。
@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);
}
打印結(jié)果:[{"address":"BeiJing","custName":"Tom","id":1},{"address":"ShangHai","custName":"Bob","id":1}]
2. 那有沒有禁止循環(huán)引用呢?
@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);
}
輸出結(jié)果:[{"id":1,"custName":"Tom","address":"BeiJing"},{"id":1,"custName":"Tom","address":"BeiJing"}],顯而易見是沒有的。
3.若有雙向一對(duì)一的關(guān)聯(lián)關(guān)系映射的話,Google Gson 也是會(huì)有死循環(huán)問題造成 java.lang.StackOverflowError 異常,但是 Gson 并沒有為我們提供一個(gè)注解,要解決此問題LZ提供一個(gè)解決方案的思路,Google Gson 使用的是 ExclusionStrategy 策略進(jìn)行某個(gè)字段或某個(gè)域的序列化,可以通過此接口自定義一個(gè) 注解來解決此問題。但是建議大家如果涉及到雙向關(guān)聯(lián)關(guān)系的對(duì)象轉(zhuǎn)換為 JSON 的需求是,使用 Fastjson。
四、三種方式的簡(jiǎn)單比較
LZ 從以下幾個(gè)方面來比較構(gòu)造 JSON 字符串的三種方式:
1. jar 包方面:顯然是 Fastjson 和 Google Gson 勝出,Jackson 需要加入 3 個(gè) jar 包。
2. 簡(jiǎn)單對(duì)象或集合轉(zhuǎn)為 JSON:若是普通的簡(jiǎn)單對(duì)象或集合進(jìn)行轉(zhuǎn)換,可能 Jackson 和 Google Gson 要?jiǎng)俪鲆恍┝?,起碼構(gòu)造比較方便。
3. 涉及到雙向關(guān)聯(lián)關(guān)系的轉(zhuǎn)換:毫無疑問阿里巴巴的 Fastjson 將勝出。
建議大家在實(shí)際的開發(fā)中根據(jù)自己的需求合理選擇某一方式。
以上這篇深入剖析構(gòu)建JSON字符串的三種方式(推薦)就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java結(jié)構(gòu)型設(shè)計(jì)模式之裝飾模式詳解
裝飾模式(Decorator Pattern)允許向一個(gè)現(xiàn)有的對(duì)象添加新的功能,同時(shí)又不改變其結(jié)構(gòu)。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它是作為現(xiàn)有類的一個(gè)包裝。這種模式創(chuàng)建了一個(gè)裝飾類,用來包裝原有的類,并在保持類方法簽名完整性的前提下,提供了額外的功能2023-03-03
Java實(shí)現(xiàn)超市會(huì)員管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)超市會(huì)員管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
Java中將List拆分為多個(gè)小list集合的實(shí)現(xiàn)代碼
這篇文章主要介紹了Java中如何將List拆分為多個(gè)小list集合,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
Spring Cloud Gateway內(nèi)置的斷言和過濾器作用說明
這篇文章主要介紹了Spring Cloud Gateway內(nèi)置的斷言和過濾器作用說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06
SpringBoot整合log4j2日志的實(shí)現(xiàn)
在項(xiàng)目推進(jìn)中,如果說第一件事是搭Spring框架的話,那么第二件事情就是在Sring基礎(chǔ)上搭建日志框架,大家都知道日志對(duì)于一個(gè)項(xiàng)目的重要性,尤其是線上Web項(xiàng)目,因?yàn)槿罩究赡苁俏覀兞私鈶?yīng)用如何執(zhí)行的唯一方式。此篇文章是博主在實(shí)踐中用Springboot整合log4j2日志的總結(jié)2021-06-06
詳解Spring boot+CXF開發(fā)WebService Demo
這篇文章主要介紹了詳解Spring boot+CXF開發(fā)WebService Demo,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-05-05
spring boot使用logback實(shí)現(xiàn)多環(huán)境日志配置詳解
這篇文章主要介紹了spring boot使用logback實(shí)現(xiàn)多環(huán)境日志配置詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-08-08
詳解Java多線程編程中LockSupport類的線程阻塞用法
LockSupport類提供了park()和unpark()兩個(gè)方法來實(shí)現(xiàn)線程的阻塞和喚醒,下面我們就來詳解Java多線程編程中LockSupport類的線程阻塞用法:2016-07-07

