什么是JSON
JSON是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
json的一般格式:由花括号包裹多个键值对,键值对的值可以是字符串,也可以是Json格式或者数组 []
{
key1 : "value1",
key2 : "value2",
key3 : {
key4 : "value4",
key5 : []
}
}
FastJson
1. 什么是fastjson?
fastjson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。
2.fastjson的优点
2.1 速度快
fastjson相对其他JSON库的特点是快,从2011年fastjson发布1.1.x版本之后,其性能从未被其他Java实现的JSON库超越。
2.2 使用广泛
fastjson在阿里巴巴大规模使用,在数万台服务器上部署,fastjson在业界被广泛接受。在2012年被开源中国评选为最受欢迎的国产开源软件之一。
2.3 测试完备
fastjson有非常多的testcase,在1.2.11版本中,testcase超过3321个。每次发布都会进行回归测试,保证质量稳定。
2.4 使用简单
fastjson的API十分简洁。
String text = JSON.toJSONString(obj); //序列化
VO vo = JSON.parseObject("{...}", VO.class); //反序列化
2.5 功能完备
支持泛型,支持流处理超大文本,支持枚举,支持序列化和反序列化扩展。
(以上粘自fastjson的wiki)
3.使用FastJson
如同上面的介绍,fastjson的api十分简单,对象转换成json串或从json串转换为对象每个操作都只要一行就能完成,下面就对fastjson的部分功能进行测试。
首先需要一个类,用于转换成json串,类中fastjson的注解会一一解释。
import com.alibaba.fastjson.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@NoArgsConstructor // 无参构造函数
@AllArgsConstructor // 全参构造函数
@Data // get/set/tostring
public class User {
@JSONField(name = "id", ordinal = 1)
private Integer id;
@JSONField(name = "姓名", ordinal = 2)
private String name;
@JSONField(name = "年龄", ordinal = 3)
private Integer age;
@JSONField(name = "性别", ordinal = 4)
private String gender;
@JSONField(name = "地址", ordinal = 5)
private String address;
@JSONField(name = "生日", format = "yyyy-MM-dd", ordinal = 6, alternateNames = {"出生日期","生日"})
private Date birth;
@JSONField(name = "爱好", ordinal = 7)
private String[] hobbits;
}
测试转换成json和从json转回
@Test
public void testJsonFiledSerializable() {
System.out.println(JSON.toJSONString(new User(1, "张三", 25, "男", "北京市",new Date(),new String[]{})));
System.out.println(JSON.parseObject("{\"id\":1,\"姓名\":\"张三\",\"年龄\":25,\"性别\":\"男\",\"地址\":\"北京市\",\"生日\":\"2020-12-12\",\"爱好\":[]}", User.class));
System.out.println(JSON.parseObject("{\"id\":1,\"姓名\":\"张三\",\"年龄\":25,\"性别\":\"男\",\"地址\":\"北京市\",\"出生日期\":\"2020-12-12\",\"爱好\":[]}", User.class));
}
输出结果为
{"id":1,"姓名":"张三","年龄":25,"性别":"男","地址":"北京市","生日":"2020-12-12","爱好":[]}
User(id=1, name=张三, age=25, gender=男, address=北京市, birth=Sat Dec 12 00:00:00 CST 2020, hobbits=[])
User(id=1, name=张三, age=25, gender=男, address=北京市, birth=Sat Dec 12 00:00:00 CST 2020, hobbits=[])
接下来尝试在类中定义一个类作为成员变量,查看是否能够转换成功
import com.alibaba.fastjson.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TUser {
@JSONField(name = "介绍", ordinal = 0)
private String name;
@JSONField(name = "用户", ordinal = 1)
private User user;
}
测试:
@Test
public void testMatryoshka() {
User user = new User(1, "张三", 25, "男", "北京市", new Date(), new String[]{"唱歌"});
TUser user1 = new TUser("测试套娃", user);
System.out.println(JSON.toJSONString(user1));
System.out.println(JSON.parseObject("{\"介绍\":\"测试套娃\",\"用户\":{\"id\":1,\"姓名\":\"张三\",\"年龄\":25,\"性别\":\"男\",\"地址\":\"北京市\", \"生日\":\"2020-12-12\",\"爱好\":[\"唱歌\"]}}",TUser.class));
}
这个测试类的结果为:
{"介绍":"测试套娃","用户":{"id":1,"姓名":"张三","年龄":25,"性别":"男","地址":"北京市","生日":"2020-12-12","爱好":["唱歌"]}}
TUser(name=测试套娃, user=User(id=1, name=张三, age=25, gender=男, address=北京市, birth=Sat Dec 12 00:00:00 CST 2020, hobbits=[唱歌]))
可以看到,json和对象之间的转换十分平滑。
下面来讲解一下 @JSONField 注解
@JSONField注解标记在成员变量上或者get/set方法上,被标记的成员变量必须有对应的get/set方法,在这里我直接使用lombok的注解帮忙生成了,减少代码量。
可以看到,在输出的json串中,并没有使用定义的字段名,是因为在注解中加入了name参数,在对象转成json串时会将字段名替换成name的值。
同时,输出的json串的顺序是由 ordinal 决定的,默认值为0,在它的值相等时会随机顺序输出。
可以看到输出的日期也是格式化后的,这个是由format参数控制的,可以设置各种格式,如 “yyyy-MM-dd” \ “yyyy年MM月dd日”
在json串转user类时,可以看到 ‘生日’ 和 ‘出生日期’ 都可以转换成user对象,这是配置了别名,使用 alternateNames
fastjson中还有JSONPath 可以很方便的操作
package com.alibaba.fastjson;
public class JSONPath {
// 求值,静态方法
public static Object eval(Object rootObject, String path);
// 计算Size,Map非空元素个数,对象非空元素个数,Collection的Size,数组的长度。其他无法求值返回-1
public static int size(Object rootObject, String path);
// 是否包含,path中是否存在对象
public static boolean contains(Object rootObject, String path) { }
// 是否包含,path中是否存在指定值,如果是集合或者数组,在集合中查找value是否存在
public static boolean containsValue(Object rootObject, String path, Object value) { }
// 修改制定路径的值,如果修改成功,返回true,否则返回false
public static boolean set(Object rootObject, String path, Object value) {}
// 在数组或者集合中添加元素
public static boolean array_add(Object rootObject, String path, Object... values);
}
当被序列化的类修改了字段的名称后,JSONPath中的方法只能根据修改后的字段名查找
@Test
public void testJsonPath() {
List<User> users = new ArrayList<>();
users.add(new User(1, "张三", 25, "男", "北京市", new Date(),new String[]{}));
users.add(new User(2, "李四", 18, "男", "上海市", new Date(),new String[]{}));
List<String> names = (List<String>) JSONPath.eval(users, "$.年龄");
System.out.println(JSONPath.size(users, "$.id"));
System.out.println(JSONPath.contains(users, "$.id"));
System.out.println(JSONPath.contains(users, "$.name"));
System.out.println(JSONPath.containsValue(users, "$.姓名", "张三"));
System.out.println(JSONPath.set(users, "$.性别", "女"));
users.forEach(System.out::println);
JSONPath.arrayAdd(users.get(0), "$.爱好", "唱","跳","rap");
System.out.println(users.get(0));
System.out.println(JSONPath.eval(users.get(0), "$.爱好[0]").toString());
System.out.println(JSONPath.eval(users.get(0), "$.爱好[0,1]").toString());
System.out.println(JSONPath.eval(users.get(0), "$.爱好[0:2]").toString());
System.out.println(JSONPath.eval(users, "[id in (1,3)]").toString());
System.out.println(JSONPath.eval(users, "[id = 2]").toString());
System.out.println(JSONPath.eval(users, "$..id").toString());
System.out.println(JSONPath.eval(users, "$.爱好.length()").toString());
}
该测试类输出:
2
true
false
true
true
User(id=1, name=张三, age=25, gender=女, address=北京市, birth=Sat Dec 12 21:59:52 CST 2020, hobbits=[])
User(id=2, name=李四, age=18, gender=女, address=上海市, birth=Sat Dec 12 21:59:52 CST 2020, hobbits=[])
User(id=1, name=张三, age=25, gender=女, address=北京市, birth=Sat Dec 12 21:59:52 CST 2020, hobbits=[唱, 跳, rap])
唱
["唱","跳"]
[唱, 跳, rap]
[{"id":1,"姓名":"张三","年龄":25,"性别":"女","地址":"北京市","生日":"2020-12-12","爱好":["唱","跳","rap"]}]
[{"id":2,"姓名":"李四","年龄":18,"性别":"女","地址":"上海市","生日":"2020-12-12","爱好":[]}]
[1, 2]
2