Java 中FastJson的使用【吃透FastJson】

发布时间 2024-01-02 19:19:38作者: sunny123456

Java 中FastJson的使用【吃透FastJson】

如果不了解JSON格式,建议先看下:JSON数据格式【学习记录】

JSON序列化、反序列化JavaBean的框架有很多,最常见的Jackson、阿里巴巴开源的FastJson、谷歌的GSON、apache提供的json-lib等,下面我们主要来熟悉一下:Java语言中FastJson的使用。

FastJson

FastJson是由阿里巴巴工程师基于Java开发的一款Json解析器和生成器,可用于将Java对象转换为其JSON表示形式,它还可以用于将JSON字符串转换为等效的Java对象。FastJson可以处理任意Java对象,包括没有源代码的预先存在的对象。

FastJson使用十分简便,我们只需要在Maven工程的pom文件中引入以下依赖即可:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.73</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

FastJson API的入口是com.alibaba.fastjson.JSON,常用的序列化操作都可以在JSON类上的静态方法直接完成。

API测试

我们已经在项目中引入了FastJson的依赖,我们再创建一个用户类用于测试:

package com.test.json;
import lombok.Data;
@Data
public class User {
    private Integer id;
    private String name;
    private String sex;
    private Integer age;
	public User () {
    }
    public User(Builder builder) {
        this.id = builder.id;
        this.name = builder.name;
        this.sex = builder.sex;
        this.age = builder.age;
    }
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">Builder</span> <span class="token function">builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">Builder</span> <span class="token punctuation">{<!-- --></span>
    <span class="token keyword">private</span> <span class="token class-name">Integer</span> id<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> name<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> sex<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token class-name">Integer</span> age<span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token class-name">Builder</span> <span class="token function">name</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token class-name">Builder</span> <span class="token function">sex</span><span class="token punctuation">(</span><span class="token class-name">String</span> sex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>sex <span class="token operator">=</span> sex<span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token class-name">Builder</span> <span class="token function">age</span><span class="token punctuation">(</span><span class="token keyword">int</span> age<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>age <span class="token operator">=</span> age<span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token class-name">Builder</span> <span class="token function">id</span><span class="token punctuation">(</span><span class="token keyword">int</span> id<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>id <span class="token operator">=</span> id<span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token class-name">User</span> <span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">User</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

一、序列化

序列化就是将JavaBean序列化为JSON字符串,下面我们来看下FastJson常见的序列化方法。

下面序列化相关方法都有统一的返回值类型String。

1)toJsonString(Object o);

public static void main(String[] args) {
	User user = User.builder().id(1).name("张三").sex("男").age(23).build();
	System.out.println(JSON.toJSONString(user));
}
  • 1
  • 2
  • 3
  • 4
{"age":23,"id":1,"name":"张三","sex":"男"}
  • 1

我们通过传入一个对象,便可以将对象转成JSON字符串,这里我们传入的不仅仅是JavaBean还可以是一个Map对象,传入Map对象我们同样也可以取到一个JSON字符串。

public static void main(String[] args) {
	Map<String, Object> userMap = new HashMap<>();
	userMap.put("id", 1);
	userMap.put("name", "张三");
	userMap.put("sex", "男");
	userMap.put("age", 23);
	System.out.println(JSON.toJSONString(userMap));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
{"age":23,"id":1,"name":"张三","sex":"男"}
  • 1

List对象也很适用,结果是一个标准的JSONArray的字符串。

public static void main(String[] args) {
	User zhangsan = User.build().id(1).name("张三").sex("男").age(23).build();
	User lisi = User.build().id(2).name("李四").sex("女").age(18).build();
	List<User> userList = new ArrayList<>();
	userList.add(zhangsan);
	userList.add(lisi);
	System.out.println(JSON.toJSONString(userList));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
[{"id":1,"name":"张三","sex":"男","age":23},{"id":2,"name":"李四","sex":"女","age":18}]
  • 1

2)toJSONString(Object o, boolean prettyFormat);

如果说 toJSONString(Object o); 的输出结果只有单调的一行让你看起来有点吃力,那么我们可以使用 toJSONString(Object o, boolean prettyFormat); 来让输出结果看起来舒服点。

public static void main(String[] args) {
	User user = User.builder().id(1).name("张三").sex("男").age(23).build();
	System.out.println(JSON.toJSONString(user, true));
}
  • 1
  • 2
  • 3
  • 4
{
    "age":23,
    "id":1,
    "name":"张三",
    "sex":"男"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

通过JSON自带的格式化,让输出结果看起来更加清晰,真是贴心~

3)JSON.toJSONString(Object object, SerializerFeature… features);

我们可以看到这个方法里面有个参数SerializerFeature...,可能感到有点陌生,我们先来看下什么是 SerializerFeature ,通过源码可以发现 SerializerFeature 原来是个枚举类:

在这里插入图片描述

源码中都被 @deprecated 注释的实例说明已经废弃了,那有哪些是我们平时经常用到的呢:

对象描述
SerializerFeature.UseSingleQuotes使用单引号而不是双引号,默认为false
SerializerFeature.PrettyFormat结果是否格式化,默认为false
SerializerFeature.WriteDateUseDateFormat如果时间是date、时间戳类型,按照这种格式"yyyy-MM-dd HH:mm"初始化时间
SerializerFeature.WriteMapNullValue是否输出值为null的字段,默认为false
SerializerFeature.WriteClassName序列化时写入类型信息,默认为false

使用案例:

  • SerializerFeature.UseSingleQuotes

使用单引号而不是使用双引号,默认为false。

public static void main(String[] args) {
	User user = User.builder().id(1).name("张三").sex("男").age(23).build();
	System.out.println(JSON.toJSONString(user, SerializerFeature.UseSingleQuotes));
}
  • 1
  • 2
  • 3
  • 4
{'age':23,'id':1,'name':'张三','sex':'男'}
  • 1
  • SerializerFeature.PrettyFormat

结果是否格式化,默认为false。

public static void main(String[] args) {
	User user = User.builder().id(1).name("张三").sex("男").age(23).build();
	System.out.println(JSON.toJSONString(user));
	System.out.println("===============");
	System.out.println(JSON.toJSONString(user, SerializerFeature.PrettyFormat));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
{"age":23,"id":1,"name":"张三","sex":"男"}
===============
{
    "age":23,
    "id":1,
    "name":"张三",
    "sex":"男"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • SerializerFeature.WriteDateUseDateFormat

如果时间是Date、时间戳类型,按照这种格式初始化时间"yyyy-MM-dd HH:mm"。

public static void main(String[] args) {
	System.out.println(JSON.toJSONString(new Date()));
	System.out.println("===============");
	System.out.println(JSON.toJSONString(new Date(), SerializerFeature.WriteDateUseDateFormat));
}
  • 1
  • 2
  • 3
  • 4
  • 5
1676427576691
===============
"2023-02-15 10:19:37"
  • 1
  • 2
  • 3

通过这种方式我们将日期输出成了固定的格式:yyyy-MM-dd HH:mm,有时候我们不想得到这种格式那该怎么办?通过下面方法支持自定义时间格式:(注意方法不要用错)

4)toJSONStringWithDateFormat(Object object, String dateFormat, SerializerFeature… features);

public static void main(String[] args) {
	System.out.println(JSON.toJSONString(new Date()));
	System.out.println("===============");
	System.out.println(JSON.toJSONStringWithDateFormat(new Date(), "HH:mm:ss"));
}
  • 1
  • 2
  • 3
  • 4
  • 5
1676428350771
===============
"10:32:30"
  • 1
  • 2
  • 3

下面我们接着看SerializerFeature枚举类的其他用法。

  • SerializerFeature.WriteMapNullValue

是否输出值为null的字段,默认为false。

这个有什么用处呢?我们应该很清楚开发规范中鼓励用JavaBean传递参数,尽量减少Map传递参数,因为Map相当于一个黑盒,对于使用者来说根本不知道里面存在哪些字段,而对于创建者来说估计也时常会忘记里面存在哪些字段,为了解决这个痛,JSON也推出了解决方法:

public static void main(String[] args) {
	Map<String, Object> dataMap = new HashMap<>();
	dataMap.put("name", null);
	dataMap.put("age", 23);
	System.out.println(JSON.toJSONString(dataMap));
	System.out.println("===============");
	System.out.println(JSON.toJSONString(dataMap, SerializerFeature.WriteMapNullValue));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
{"age":23}
===============
{"name":null,"age":23}
  • 1
  • 2
  • 3

通过普通方式的 toJSONString() 方法,空值仿佛被吃掉了,这很可能会成为一个开发灾难!

  • SerializerFeature.WriteClassName

序列化时写入类信息,默认为false。这个方法可以在反序列化的时候用到,用法如下:

public static void main(String[] args) {
	User user = User.builder().id(1).name("张三").sex("男").age(23).build();
	System.out.println(JSON.toJSONString(user, SerializerFeature.WriteClssName));
}
  • 1
  • 2
  • 3
  • 4
{"@type":"com.test.json.User","age":23,"id":1,"name":"张三","sex":"男"}
  • 1

通过这样我们可以看到我们序列化的对象是什么类型的。

上面这些便是toJSONString的扩展用法,上面说到的是序列化,那么对应的便是反序列化。

二、反序列化

反序列化就是把JSON格式的字符串转换为JavaBean对象。

1)JSONObject parseObject(String text);

public static void main(String[] args){
	String jsonStr = "{'age':23,'id':1,'name':'张三','sex':'男'}";
	JSONObject jsonObject = JSON.parseObject(jsonStr);
	System.out.println(jsonObject.get("name"));
}
  • 1
  • 2
  • 3
  • 4
  • 5
张三
  • 1

用法十分简单,可以将一个标准的JSON字符串转为一个JSONObject对象,由于JSONObject类实现了Map接口,因此我们可以通过get()来获取到值。

我们上述已经说过Map的致命不足,所以我们更希望能得到一个JavaBean对象。

2)<T> T parseObject(String text, Class<T> clazz);

我们通过传入我们想要转换的对象类型,就可以得到我们想要的JavaBean。

public static void main(String[] args){
	String jsonStr = "{'age':23,'id':1,'name':'张三','sex':'男'}";
    User user = JSON.parseObject(jsonStr, User.class);
    System.out.println(user.getName());
}
  • 1
  • 2
  • 3
  • 4
  • 5
张三
  • 1

除了基本反序列化之外,还有一种泛型反序列化可提供使用。

3)<T> T parseObject(String text, TypeReference<T> type, Feature... features);

通过泛型,我们就可以不用传入一个Class对象,而直接获取到我们的JavaBean。

public static void main(String[] args){
    String jsonStr = "{'age':23,'id':1,'name':'张三','sex':'男'}";
    User user = JSON.parseObject(jsonStr, new TypeReference<User>(){});
    System.out.println(user.getName());
}
  • 1
  • 2
  • 3
  • 4
  • 5
张三
  • 1

FastJson序列化还有一个用处那便是进行深克隆。软件设计模式中:原型模式涉及到的深克隆和浅克隆。

浅克隆的实现方式十分简单,我们只需要实现Cloneable接口,然后重写clone()方法:

@Data
class Person {
	private String name;
}
@Data
class NiceCard implements Cloneable {
	public Person person;
	public void award() {
		System.out.println(this.person.getName() + "获得好人卡一张");
	}
	public NiceCard clone() throws CloneNotSupportedException {
		return (NiceCard)super.clone();
	}
}
public class TestClient {
	public static void main(String[] args) {
		NiceCard niceCard = new NiceCard();
		Person person = new Person();
		person.setName("小李");
		niceCard.setPerson(person);
		NiceCard cloneCard = niceCard.clone();
		Persson clonePerson = cloneCard.getPerson();
		clonePerson.setName("小王");
		niceCard.award();
		cloneCard.award();
		System.out.println(person == clonePerson);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
小王获得一张好人卡
小王获得一张好人卡
true
  • 1
  • 2
  • 3

结果中我们可以看到,好人卡都是属于小王的,这就是浅克隆的弊端了。

我们要想实现深克隆有许多种方式:

  • 手动为引用属性赋值
  • 借助FastJson
  • 使用Java流的序列化对象

方法有很多,我们重点看下FastJson的实现方式:

public static void main(String[] args) {
    NiceCard niceCard = new NiceCard();
    Person person = new Person();
    person.setName("小李");
    niceCard.setPerson(person);
    //借助FastJson
    NiceCard cloneCard = JSON.parseObject(JSON.toJSONString(niceCard), NiceCard.class);
    Person clonePerson = cloneCard.getPerson();
    clonePerson.setName("小王");
    niceCard.award();
    cloneCard.award();
    System.out.println(person == clonePerson);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
小李获得好人卡一张
小王获得好人卡一张
false
  • 1
  • 2
  • 3

通过FastJson反序列化,我们得到的两个对象实际上是不同的,这也很方便的实现了深克隆。

4)<T> List<T> parseArray(String text, Class<T> clazz);

这是将一个JSON字符串转为JSONArray的方法。

public static void main(String[] args) {
	String jsonStr = "[{\"id\":1,\"name\":\"张三\",\"sex\":\"男\",\"age\":23},{\"id\":2,\"name\":\"李四\",\"sex\":\"女\",\"age\":18}]";
	List<User> userList = JSON.parseArray(jsonStr, User.class);
	userList.forEach(System.out::println);
}
  • 1
  • 2
  • 3
  • 4
  • 5
User(id=1, name=张三, sex=男, age=23)
User(id=2, name=李四, sex=女, age=18)
  • 1
  • 2

同样我们也可以通过使用泛型序列化来实现同样的功能:

5)List<Object> parseArray(String text, Type[] types);

public static void main(String[] args) {
    String jsonStr = "[{\"id\":1,\"name\":\"张三\",\"sex\":\"男\",\"age\":23},{\"id\":2,\"name\":\"李四\",\"sex\":\"女\",\"age\":18}]";
    Type type = new TypeReference<User>() {}.getType();
    JSON.parseArray(jsonStr, new Type[]{type, type}).forEach(System.out::println);
}
  • 1
  • 2
  • 3
  • 4
  • 5

这种方式有个坑就是:我们使用parseArray()这个方法的时候第二个参数需要传入我们要反序列化的对象类型,但是我们这里需要传入的是数组,不知道你有没有为数组里面放了两个一样的type感到奇怪?没错,这就是方法的坑,我们List里面有多少个对象,Type[]这个数组里面的个数要与之匹配,不然会抛出以下错误:

在这里插入图片描述
上述JSON串中对象的类型都为User,如果JSON串中存在不同的类型时:

public static void main(String[] args) {
    String jsonStr = "[{\"id\":1,\"name\":\"张三\",\"sex\":\"男\",\"age\":23},{\"stuName\":\"李四\"}]";
    Type userType = new TypeReference<User>() {}.getType();
    Type studentType = new TypeReference<Student>() {}.getType();
    JSON.parseArray(jsonStr, new Type[]{userType,studentType}).forEach(System.out::println);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
User(id=1, name=张三, sex=男, age=23)
Student(stuName=李四)
  • 1
  • 2

如果将userType和studentType调换顺序,则结果:

Student(stuName=null)
User(id=null, name=null, sex=null, age=null)
  • 1
  • 2

三、JavaBean转换为Byte[]

我们平时在进行网络通讯的时候,需要将对象转为字节然后进行传输。

1)我们使用字符串的时候,字符串中有个很便捷的API可以将字符串转为字节数组。

String str = "张三";
byte[] bytes = str.getBytes();
  • 1
  • 2

2)但是我们要将一个JavaBean对象转为字节数组的时候,我们得借助ByteArrayOutputStream流的帮助。

public static void main(String[] args) {
	byte[] bytes = null;
	ByteArrayOutputStream bos = new ByteArrayOutputStream();
	try{
		ObjectOutputStream oos = new ObjectOutputStream(bos);
		oos.writeObject(obj);
		oos.flush();
		bytes = bos.toByteArray();
		oos.close();
		bos.close();
	} catch(IOException ex) {
		ex.printStackTrace();
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

这种方式也可以很好的将JavaBean对象转为字节数组,但是代码不免有点多了!

3)而FastJson中也提供了很方便的API以供使用(将JavaBean对象转为字节数组):

  • byte[] toJSONBytes(Object object, SerializerFeature... features);
public static void main(String[] args) {
	User user = User.builder().id(1).name("张三").sex("男").age(23).build();
	//JavaBean对象转为字节数组
	byte[] bytes = JSON.toJSONBytes(user);
}
  • 1
  • 2
  • 3
  • 4
  • 5

4)FastJson也同样支持(将字节数组转为JavaBean):

  • <T> T parseObject(byte[] bytes, Type clazz, Feature... features);
public static void main(String[] args) {
	User user = User.builder().id(1).name("张三").sex("男").age(23).build();
	//JavaBean对象转为字节数组
	byte[] bytes = JSON.toJSONBytes(user);
	//将字节数组转为JavaBean对象
	Object obj = JSON.parseObject(bytes, User.class, Feature.IgnoreNotMatch);
	System.out.println(obj);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
User(id=1, name=张三, sex=, age=23)
  • 1

从parseObject()这个方法中我们又看到了一个奇怪的参数Feature,我们点击进入源码可以发现这其实也是一个枚举类:

在这里插入图片描述

看的同样云里雾里的,这么多实例,以下我们对比较常用的做出了注释:

对象描述
AllowUnQuotedFieldNames决定parser是否将允许使用非双引号属性名
AllowSingleQuotes决定parser是否允许单引号来包住属性名称和字符串值
InternFieldNames决定JSON对象属性名称是否可以被String#intern 规范化表示,如果允许,则JSON所有的属性名将会 intern() ;如果不设置,则不会规范化,默认下,该属性是开放的。
AllowISO8601DateFormat设置为true则遇到字符串符合ISO8601格式的日期时,会直接转换成日期类
AllowArbitraryCommas允许多重逗号,如果设为true,则遇到多个逗号会直接跳过
UseBigDecimal设置为true则用BigDecimal类来装载数字,否则用的是double
IgnoreNotMatch忽略不匹配
DisableCircularReferenceDetect禁用循环引用检测
InitStringFieldAsEmpty对于没有值得字符串属性设置为空串
SupportArrayToBean支持数组to对象
OrderedField属性保持原来的顺序
DisableSpecialKeyDetect禁用特殊字符检查
UseObjectArray使用对象数组
  • writeJSONString(OutputStream os, Object o);

这个方法是将对象写入输出流中。

public static void main(String[] args) throws IOException {
    User user = User.builder().id(1).name("张三").sex("男").age(23).build();
    FileOutputStream os = new FileOutputStream("D:\\test.txt");
    int i = JSON.writeJSONString(os, user);
    System.out.println(i); //输出45
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在D盘下生成了test.txt文件,并写入内容:

{"age":23,"id":1,"name":"张三","sex":"男"}
  • 1
  • void writeJSONString(Writer writer, Object object, SerializerFeature... features);

传入的参数还可以是一个Writer对象:

public static void main(String[] args) throws IOException {
    User user = User.builder().id(1).name("张三").sex("男").age(23).build();
    Writer writer = new FileWriter("D:\\test.txt");
    JSON.writeJSONString(writer, user);
}
  • 1
  • 2
  • 3
  • 4
  • 5

同样在D盘下生成了test.txt文件,并写入内容:

{"age":23,"id":1,"name":"张三","sex":"男"}
  • 1

四、@JSONField注解(命名重塑)

package com.alibaba.fastjson.annotation;
public @interface JSONField {
	//配置序列化和反序列化的顺序,1.1.42版本之后才支持
	int ordinal() default 0;
	//指定字段的名称
	String name() default "";
	//指定字段的格式,对日期格式有用
	String format() default "";
	//是否序列化
	boolean serialize() default true;
	//是否反序列化
	boolean deserialize() default true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

注:若属性是私有的,必须要有set()方法,否则无法反序列化。

@JSONField注解用法很简单,可以配置在getter()、setter()、或者属性字段上。

@Data
@ToString
public class User implements Serializable {
	private Integer id;
	@JSONField(name="userName")
	private String name;
	private String sex;
	private Integer age;
	@JSONField(name="userSex")
	public String getSex() {
		return sex;
	}
	@JSONField(name="userSex")
	public void setSex(String sex) {
		this.sex = sex;
	}
	@JSONField(name="userAge")
	public String getAge() {
		return age;
	}
	@JSONField(name="userAge")
	public void setAge(Integer age) {
		this.age = age;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

测试并输出结果:

public static void main(String[] args) {
	String userJsonStr = "{'userAge':23,'id':1,'userName':'张三','userSex':'男'}";
	User user = JSON.parseObject(userJsonStr, User.class);
	System.out.println(user);
}
  • 1
  • 2
  • 3
  • 4
  • 5
User{id=1,name=张三,sex=,age=23}
  • 1

这个方法的最大好处便是用来对接奇奇怪怪的文档,有时候我们需要调用第三方的接口,但是这个接口返回的值可能是不符合命名规范的,那我们这边就需要定义一个实体类去接收它(Map虽然也行,但是也不规范)。

这个时候我们定义的实体的属性名就得按照返回的字段名来命名,这对强迫症程序员来说是致命打击,这个时候@JSONField的用处就来了,我们简单看个例子,有个车牌信息实体的返回字段是这样的:

{"PLATEID" : 01, "PLATE": '闽A6666D', "IMAGEURL":'http://...'}
  • 1

我们可以看到返回的字段全部不满足小驼峰规则,我们定义的实体类可不能这样,借助@JSONField的写法如下:

@Data
@ToString
public class PlateInfo {
	/**
	* 车牌ID
	*/
	@JSONField(name="PLATEID")
	private Integer plateId;
	/**
	* 车牌号
	*/
	@JSONField(name="PLATE")
	private String plate;
	/**
	* 图片URL
	*/
	@JSONField(name="IMAGEURL")
	private Integer imageUrl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

测试下是否能够成功接收结果:

public static void main(Stirng[] args) {
	String plateJsonStr = "{'PLATEID':01,'PLATE':'闽A6666D','IMAGEURL':'http://...'}";
	PlateInfo plateInfo = JSON.parseObject(plateJsonStr, PlateInfo.class);
	System.out.println(plateInfo);
}
  • 1
  • 2
  • 3
  • 4
  • 5
PlateInfo(plateId=1,plate=A6666D,imageUrl=http://...)
  • 1

可以看到我们已经成功接收到结果了,而且实体类的命名也符合规范,一举两得。

  • format属性:格式化日期

我们也可以使用该注解来将我们的日期格式化成我们想要的样子。

@JSONField(format="yyyy-MM-dd HH:mm:ss")
private Date birthday;
public static void main(String[] args) {
	User user = User.builder().id(1).name("张三").sex("男").age(23).birthday(new Date()).build();
	System.out.println(JSON.toJSONString(user));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
{"age":23,"birthday":"2023-02-17 14:03:48","id":1,"name":"张三","sex":"男"}
  • 1
  • serialize属性:控制序列化
  • deserialize属性:控制反序列化

在序列化或反序列化的时候我们可以指定字段不序列化,这个有点像Java流中的transient修饰。FastJson中也可以实现相似的功能:

@JSONField(serialize=false,deserialize=false)
private Date birthday;
 public static void main(String[] args) {
    String userJsonStr = "{\"age\":23,\"birthday\":\"2023-02-17 02:05:29\",\"id\":1,\"name\":\"张三\",\"sex\":\"男\"}";
    User user = JSON.parseObject(userJsonStr, User.class);
    System.out.println(user);
    System.out.println("===========================");
    User user1 = User.builder().id(1).name("张三").sex("男").age(23).birthday(new Date()).build();
    System.out.println(JSON.toJSONString(user1));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
User(id=1, name=张三, sex=, age=23, birthday=null)
===========================
{"age":23,"id":1,"name":"张三","sex":"男"}
  • 1
  • 2
  • 3

但是反序列化有个缺点就是,虽然值是空的,但是属性名还在~

  • ordinal属性:指定字段的顺序
@Data
public class User implements Serializable {
	@JSONField(ordinal = 2)
	private Integer id;
	@JSONField(ordinal = 1)
	private String name;
	@JSONField(ordinal = 4)
	private String sex;
	@JSONField(ordinal = 3)
	private Integer age;
}
public static void main(String[] args) {
	User user = User.builder().id(1).name("张三").sex("男").age(23).build();
	System.out.println(JSON.toJSONString(user));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
{"name":"张三","id":1,"age":23,"sex":"男"}
  • 1

通过接收结果可以看到属性字段按照我们规定的顺序所排列,用处可以在于我们返回字段给前端过多的时候,将有用的字段优先排列在前面,可以更好的取值,而不用一层一层的查找需要的字段。

  • serializeUsing属性:指定属性的序列化类

万物皆可定制,序列化也不例外~我们可以使用serializeUsing指定属性的序列化类。

@Data
public class User implements Serializable {
	private Integer id;
	private String name;
	private String sex;
	@JSONFiled(serializeUsing = ModelValueSerializer.class)
	private Integer age;
}
/**
* 自定义序列化规则
*/
class ModelValueSerializer implements ObjectSerializer {
	@Override
	public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, 
		int features) throws IOException {
		String value = String.valueOf(object);
		String text = value + "岁";
		serializer.write(text);
	}
}
/**
* 测试类
*/
public static void main(String[] args) {
	User user = User.builder().id(1).name("张三").sex("男").age(23).build();
	System.out.println(JSON.toJSONString(user));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
{"name":"张三","id":1,"age":"23岁","sex":"男"}
  • 1

通过这种方式我们针对age这个属性进行了处理,给指定字段加上了单位。

原文链接:https://blog.csdn.net/weixin_44296929/article/details/129011709