深入剖析构建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.*error,抛异常的原因是双方都维护关联关系进入了死循环,那么如何解决这个问题呢?可以在一方添加 @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.*error,我们的思路与测试 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.*error 异常,但是 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字符串的三种方式(推荐)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。