原来Fastjson还能这么玩

1. 前言

Fastjson

fastjson 是阿里巴巴的开源 JSON 解析库,它可以解析 JSON 格式的字符串,支持将 Java Bean 序列化为JSON字符串,也可以从 JSON字符串反序列化到 JavaBean。

还有如 Jackson 、Gson 等等 JSON 解析库也很强大。

2. 对 Enum 的特殊处理

2.1 前戏

在我们开发中常常会用到枚举,比如表示会员等级、性别、是否激活等等,本来拿性别来举例,0为女,1为男。

先介绍一下枚举的一个特性 ordinal ,它是一个常量

image-20210721223133912

它记录这枚举常量的序数,从0开始,像数组索引一样,我们可以合理地利用这个特性(虽然很多地方说不推荐用,但是个人觉得是可以合理利用的)

定义一个Sex枚举

1
2
3
4
public enum Sex {
female,
male,
}

那么意味着 female 为0,male 为1 ,这个时候不管前台传值0或者值 female ,我们都知道能解析到 female 这个常量

image-20210721224133973

为0时也能解析到,这里就不演示了。我们可以发现返回的 sex 也是 female ,那如何返回0呢,这个时候我们就需要自定义枚举序列化规则了

2.2 定制序列化规则

1
2
3
4
5
6
7
public class SexEnumSerializer implements ObjectSerializer {
@Override
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
Sex sex = (Sex) object;
serializer.out.writeInt(sex.ordinal());
}
}

实现 fasjson 的 ObjectSerializer 去自定义 Sex 这个枚举的序列化规则,然后在类的属性上使用

image-20210721230305778

image-20210721230247349

image-20210721230337196

当响应时可以看到经过了我们自定义的序列化规则,在这里我们可以获取到响应 json 以及字段名、类型等等信息,我们在这个时候做点手脚就可以返回 ordinal 了

2.3 定制反序列化规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SexEnumDeSerializer implements ObjectDeserializer {

@Override
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
Object parse = parser.parse();
if (Objects.isNull(parse)){
return (T) Sex.female;
}
return (T) parse;
}

@Override
public int getFastMatchToken() {
return 0;
}
}

为我们的 Sex 配置反序列化规则

image-20210724144708729

image-20210724144948929

此时 sex 为 null ,在我们反序列化时就会按照我们的规则选择 female ,然后返回时序列化 为 ordianl ,也就是0

3. 配置 HttpMessageConverter

可能有的小伙伴进行上面操作时,达不到预期效果,因为 SpringBoot 默认 JSON 解析库是 Jackson !!(接口为什么返回的是 JSON 这是基础,这里不过多阐述),我们需要去定制 HttpMessageConverter,我们的请求响应文本都是它处理的,既然默认是 Jackson ,那么我们就改成 Fastjson 不就行了 ,直接上代码

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package cn.this52.fastjsondemo;

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.nio.charset.StandardCharsets;

@Configuration
public class BaseWebMvcConfig implements WebMvcConfigurer {

/**
* 定制fastjson消息转换器
*/
@Bean
public HttpMessageConverters fastJsonHttpMessageConverter() {
// 定义一个convert转换消息的对象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();

// 添加fastjson的配置信息
FastJsonConfig fastJsonConfig = new FastJsonConfig();

SerializerFeature[] serializerFeatures = new SerializerFeature[]{
// 输出key时是否使用双引号,默认为true
// SerializerFeature.QuoteFieldNames,
// 使用单引号而不是双引号,默认为false
// SerializerFeature.UseSingleQuotes,
// 是否输出值为null的字段,默认为false
SerializerFeature.WriteMapNullValue,
// enum输出name()或者original,默认为false
SerializerFeature.WriteEnumUsingToString,
// enum值序列化为其Name,默认为true
// SerializerFeature.WriteEnumUsingName,
// Date使用ISO8601格式输出,默认为false
// SerializerFeature.UseISO8601DateFormat,
// List字段如果为null,输出为[],而非null
SerializerFeature.WriteNullListAsEmpty,
// 字符类型字段如果为null,输出为”“,而非null
SerializerFeature.WriteNullStringAsEmpty,
// 数值字段如果为null,输出为0,而非null
SerializerFeature.WriteNullNumberAsZero,
// Boolean字段如果为null,输出为false,而非null
SerializerFeature.WriteNullBooleanAsFalse,
// 如果是true,类中的Get方法对应的Field是transient,序列化时将会被忽略。默认为true
// SerializerFeature.SkipTransientField,
// 按字段名称排序后输出。默认为false
// SerializerFeature.SortField,
// 不是String的字段写为String
// SerializerFeature.WriteNonStringValueAsString,
// 忽略掉getter方法出错的属性
// SerializerFeature.IgnoreErrorGetter,
// 大数字写成文本
SerializerFeature.WriteBigDecimalAsPlain,
// Date的日期转换器
SerializerFeature.WriteDateUseDateFormat,
// 禁止循环引用
SerializerFeature.DisableCircularReferenceDetect,
// 格式化输出
SerializerFeature.PrettyFormat,
};

fastJsonConfig.setSerializerFeatures(serializerFeatures);
// 字符集
fastJsonConfig.setCharset(StandardCharsets.UTF_8);
// 日期格式化
fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");

// 添加配置信息
fastConverter.setFastJsonConfig(fastJsonConfig);

// 将convert添加到converters中
return new HttpMessageConverters(fastConverter);
}
}

具体特性可以去看看文档或者源码

4. 最后

需要注意的是在一个对象中对多个属性使用相同的规则,只会选择第一个。

很多问题都是在实际开发中遇到的问题,需要我们去解决,也就衍生出了各种方案,但我们要知道的是自定义一时爽,维护火葬场,还是得根据具体需要去选择方案吧。

上面的例子很简单,不仅限是枚举,其他任意对象都是可以操作的,可以有更复杂的处理,举一反三有无限种可能!


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!