什么是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