整合fastjson
简介
对于 ResponseBody 的渲染主要是通过 HttpMessageConverters, ⽽⾸先引⼊FastJson Pom依赖并排除 Spring Boot ⾃带的 Jackson。
fastjson 是阿里巴巴的开源JSON解析库,它可以解析 JSON 格式的字符串,支持将 Java Bean 序列化为 JSON 字符串,也可以从 JSON 字符串反序列化到 JavaBean。
你可以通过如下地方下载fastjson:
在maven项目的pom文件中直接配置fastjson依赖,fastjson最新版本都会发布到maven中央仓库,你可以直接依赖。
导入fastjson依赖
剔除SpringBoot自带json格式
1
2
3
4
5
6
7
8
9
10
11<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</exclusion>
</exclusions>
</dependency>引入fastjson
1
2
3
4
5
6<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
fastjson配置类
编写转换器处理 json 的⽇期格式同时处理中⽂乱码问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class WebMvcConfig implements WebMvcConfigurer {
/**
* ⾃定义JSON转换器
*
* @param converters
*/
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
//⽇期格式化
fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
//处理中⽂乱码问题
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
converter.setSupportedMediaTypes(fastMediaTypes);
converter.setFastJsonConfig(fastJsonConfig);
converters.add(converter);
}
}
SerializerFeature属性
名称 | 含义 | 备注 |
QuoteFieldNames | 输出key时是否使用双引号,默认为true | |
UseSingleQuotes | 使用单引号而不是双引号,默认为false | |
WriteMapNullValue | 是否输出值为null的字段,默认为false | |
WriteEnumUsingToString | Enum输出name()或者original,默认为false | |
UseISO8601DateFormat | Date使用ISO8601格式输出,默认为false | |
WriteNullListAsEmpty | List字段如果为null,输出为[],而非null | |
WriteNullStringAsEmpty | 字符类型字段如果为null,输出为”“,而非null | |
WriteNullNumberAsZero | 数值字段如果为null,输出为0,而非null | |
WriteNullBooleanAsFalse | Boolean字段如果为null,输出为false,而非null | |
SkipTransientField | 如果是true,类中的Get方法对应的Field是transient,序列化时将会被忽略。默认为true | |
SortField | 按字段名称排序后输出。默认为false | |
WriteTabAsSpecial | 把\t做转义输出,默认为false | 不推荐 |
PrettyFormat | 结果是否格式化,默认为false | |
WriteClassName | 序列化时写入类型信息,默认为false。反序列化是需用到 | |
DisableCircularReferenceDetect | 消除对同一对象循环引用的问题,默认为false | |
WriteSlashAsSpecial | 对斜杠’/’进行转义 | |
BrowserCompatible | 将中文都会序列化为\uXXXX格式,字节数会多一些,但是能兼容IE 6,默认为false | |
WriteDateUseDateFormat | 全局修改日期格式,默认为false。JSON.DEFFAULT_DATE_FORMAT = “yyyy-MM-dd”;JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat); | |
DisableCheckSpecialChar | 一个对象的字符串属性中如果有特殊字符如双引号,将会在转成json时带有反斜杠转移符。如果不需要转义,可以使用这个属性。默认为false | |
NotWriteRootClassName | 含义 | |
BeanToArray | 将对象转为array输出 | |
WriteNonStringKeyAsString | 含义 | |
NotWriteDefaultValue | 含义 | |
BrowserSecure | 含义 | |
IgnoreNonFieldGetter | 含义 | |
WriteEnumUsingName | 含义 |
API
Fastjson入口类是 com.alibaba.fastjson.JSON,主要的 API 是
JSON.toJSONString
和parseObject
。1
2
3
4
5
6
7package com.alibaba.fastjson;
public abstract class JSON {
// Java对象转换为JSON字符串
public static final String toJSONString(Object object);
//JSON字符串转换为Java对象
public static final <T> T parseObject(String text, Class<T> clazz, Feature... features);
}序列化:
1
String jsonString = JSON.toJSONString(obj);
反序列化:
1
VO vo = JSON.parseObject("...", VO.class);
泛型反序列化:
1
2
3import com.alibaba.fastjson.TypeReference;
List<VO> list = JSON.parseObject("...", new TypeReference<List<VO>>() {});
序列化
代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14// 转换为 JSON
String jsonString = JSON.toJSONString(group);
System.out.println("JSON字符串:" + jsonString);
// 转换为 对象BEAN
Grade grade = JSON.parseObject(jsonString, Grade.class);
System.out.println("JavaBean对象:" + grade);
// JSON字符串:
{"id":0,"name":"admin","users":[{"id":2,"name":"guest"},{"id":3,"name":"root"}]}
// JavaBean对象:
Grade{id=0, name='admin', users=[Student{id=2, name='guest'}, Student{id=3, name='root'}]}
空值输出
在fastjson中,缺省是不输出空值的。无论Map中的null和对象属性中的null,序列化的时候都会被忽略不输出,这样会减少产生文本的大小。但如果需要输出空值怎么做呢?
如果你需要输出空值,需要使用
SerializerFeature.WriteMapNullValue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22public static String obj2json(Object obj) {
String json = "";
try {
json =
JSONObject.toJSONString(
obj,
// collection 空值输出 []
SerializerFeature.WriteMapNullValue,
// 字符串类型空值输出空字符串""
SerializerFeature.WriteNullStringAsEmpty,
// 数值类型的空值输出为0
SerializerFeature.WriteNullNumberAsZero,
// 布尔类型为null输出false
SerializerFeature.WriteNullBooleanAsFalse,
// 禁用对象循环引用
SerializerFeature.DisableCircularReferenceDetect);
} catch (Exception e) {
e.printStackTrace();
return "";
}
return json;
}
处理日期
Fastjson 处理日期的API很简单,例如:
1
JSON.toJSONStringWithDateFormat(date, "yyyy-MM-dd HH:mm:ss.SSS")
使用ISO-8601日期格式
1
JSON.toJSONString(obj, SerializerFeature.UseISO8601DateFormat);
全局修改日期格式
1
2JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd";
JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat);反序列化能够自动识别如下日期格式:
- ISO-8601日期格式
- yyyy-MM-dd
- yyyy-MM-dd HH:mm:ss
- yyyy-MM-dd HH:mm:ss.SSS
- 毫秒数字
- 毫秒数字字符串
- .NET JSON日期格式
- new Date(198293238)
虽然上面处理了单个的日期类型和全局的日期类型格式的配置,但是有时候我们需要的是对象中个别的日期类型差异化,并不一定是同一种格式的。那如何处理呢?接下来介绍 Fastjson 的定制序列化。
Fastjson 定制序列化
- fastjson支持多种方式定制序列化。
- 通过@JSONField定制序列化
- 通过@JSONType定制序列化
- 通过SerializeFilter定制序列化
- 通过ParseProcess定制反序列化
使用@JSONField配置
JSONField 注解介绍
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.alibaba.fastjson.annotation;
public JSONField {
// 配置序列化和反序列化的顺序,1.1.42版本之后才支持
int ordinal() default 0;
// 指定字段的名称
String name() default "";
// 指定字段的格式,对日期格式有用
String format() default "";
// 是否序列化
boolean serialize() default true;
// 是否反序列化
boolean deserialize() default true;
}JSONField配置方式
注意:若属性是私有的,必须有set*方法。否则无法反序列化。
可以把@JSONField配置在字段或者getter/setter方法上,例如:
配置在字段上
1
2
3
4
5
6
7public class VO {
private int id;
public Date date;
}配置在 Getter/Setter 上
1
2
3
4
5
6
7
8
9public class VO {
private int id;
public int getId() { return id;}
public void setId(int id) {this.id = id;}
}
使用format配置日期格式化
可以定制化配置各个日期字段的格式化
1
2
3
4
5public class A {
// 配置date序列化和反序列使用yyyyMMdd日期格式
public Date date;
}
使用serialize/deserialize指定字段不序列化
1
2
3
4
5
6
7
8
9public class A {
public Date date;
}
public class A {
public Date date;
}使用ordinal指定字段的顺序
- 缺省Fastjson序列化一个java bean,是根据fieldName的字母序进行序列化的,你可以通过ordinal指定字段的顺序。这个特性需要1.1.42以上版本。
1
2
3
4
5
6
7
8
9
10public static class VO {
private int f0;
private int f1;
private int f2;
}使用serializeUsing制定属性的序列化类
在fastjson 1.2.16版本之后,JSONField支持新的定制化配置serializeUsing,可以单独对某一个类的某个属性定制序列化,比如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14public static class Model {
public int value;
}
public static class ModelValueSerializer implements ObjectSerializer {
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType,
int features) throws IOException {
Integer value = (Integer) object;
String text = value + "元";
serializer.write(text);
}
}测试代码
1
2
3
4Model model = new Model();
model.value = 100;
String json = JSON.toJSONString(model);
Assert.assertEquals("{\"value\":\"100元\"}", json);
使用@JSONType配置
- 和JSONField类似,但JSONType配置在类上,而不是field或者getter/setter方法上。
通过SerializeFilter定制序列化
SerializeFilter是通过编程扩展的方式定制序列化。fastjson支持6种SerializeFilter,用于不同场景的定制序列化。
- PropertyPreFilter 根据PropertyName判断是否序列化
- PropertyFilter 根据PropertyName和PropertyValue来判断是否序列化
- NameFilter 修改Key,如果需要修改Key,process返回值则可
- ValueFilter 修改Value
- BeforeFilter 序列化时在最前添加内容
- AfterFilter 序列化时在最后添加内容
PropertyFilter 根据PropertyName和PropertyValue来判断是否序列化
1
2
3public interface PropertyFilter extends SerializeFilter {
boolean apply(Object object, String propertyName, Object propertyValue);
}可以通过扩展实现根据object或者属性名称或者属性值进行判断是否需要序列化。例如:
1
2
3
4
5
6
7
8
9
10
11
12PropertyFilter filter = new PropertyFilter() {
public boolean apply(Object source, String name, Object value) {
if ("id".equals(name)) {
int id = ((Integer) value).intValue();
return id >= 100;
}
return false;
}
};
JSON.toJSONString(obj, filter); // 序列化的时候传入filterPropertyPreFilter 根据PropertyName判断是否序列化
和PropertyFilter不同只根据object和name进行判断,在调用getter之前,这样避免了getter调用可能存在的异常。
1
2
3public interface PropertyPreFilter extends SerializeFilter {
boolean apply(JSONSerializer serializer, Object object, String name);
}
NameFilter 序列化时修改Key
如果需要修改Key,process返回值则可
1
2
3public interface NameFilter extends SerializeFilter {
String process(Object object, String propertyName, Object propertyValue);
}
fastjson内置一个PascalNameFilter,用于输出将首字符大写的Pascal风格。例如:
1
2
3
4import com.alibaba.fastjson.serializer.PascalNameFilter;
Object obj = ...;
String jsonStr = JSON.toJSONString(obj, new PascalNameFilter());ValueFilter 序列化时修改Value
1
2
3public interface ValueFilter extends SerializeFilter {
Object process(Object object, String propertyName, Object propertyValue);
}BeforeFilter 序列化时在最前添加内容
在序列化对象的所有属性之前执行某些操作,例如调用 writeKeyValue 添加内容
1
2
3
4
5public abstract class BeforeFilter implements SerializeFilter {
protected final void writeKeyValue(String key, Object value) { ... }
// 需要实现的抽象方法,在实现中调用writeKeyValue添加内容
public abstract void writeBefore(Object object);
}
AfterFilter 序列化时在最后添加内容
在序列化对象的所有属性之后执行某些操作,例如调用 writeKeyValue 添加内容
1
2
3
4
5public abstract class AfterFilter implements SerializeFilter {
protected final void writeKeyValue(String key, Object value) { ... }
// 需要实现的抽象方法,在实现中调用writeKeyValue添加内容
public abstract void writeAfter(Object object);
}
通过ParseProcess定制反序列化
ParseProcess是编程扩展定制反序列化的接口。fastjson支持如下ParseProcess:
- ExtraProcessor 用于处理多余的字段
- ExtraTypeProvider 用于处理多余字段时提供类型信息
使用ExtraProcessor 处理多余字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public static class VO {
private int id;
private Map<String, Object> attributes = new HashMap<String, Object>();
public int getId() { return id; }
public void setId(int id) { this.id = id;}
public Map<String, Object> getAttributes() { return attributes;}
}
ExtraProcessor processor = new ExtraProcessor() {
public void processExtra(Object object, String key, Object value) {
VO vo = (VO) object;
vo.getAttributes().put(key, value);
}
};
VO vo = JSON.parseObject("{\"id\":123,\"name\":\"abc\"}", VO.class, processor);
Assert.assertEquals(123, vo.getId());
Assert.assertEquals("abc", vo.getAttributes().get("name"));使用ExtraTypeProvider 为多余的字段提供类型
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
27public static class VO {
private int id;
private Map<String, Object> attributes = new HashMap<String, Object>();
public int getId() { return id; }
public void setId(int id) { this.id = id;}
public Map<String, Object> getAttributes() { return attributes;}
}
class MyExtraProcessor implements ExtraProcessor, ExtraTypeProvider {
public void processExtra(Object object, String key, Object value) {
VO vo = (VO) object;
vo.getAttributes().put(key, value);
}
public Type getExtraType(Object object, String key) {
if ("value".equals(key)) {
return int.class;
}
return null;
}
};
ExtraProcessor processor = new MyExtraProcessor();
VO vo = JSON.parseObject("{\"id\":123,\"value\":\"123456\"}", VO.class, processor);
Assert.assertEquals(123, vo.getId());
Assert.assertEquals(123456, vo.getAttributes().get("value"));
// value本应该是字符串类型的,通过getExtraType的处理变成Integer类型了。
Spring Data Redis 中集成 Fastjson
- 通常我们在 Spring 中使用 Redis 是通过 Spring Data Redis 提供的 RedisTemplate 来进行的,如果你准备使用 JSON 作为对象序列/反序列化的方式并对序列化速度有较高的要求的话,建议使用 Fastjson 提供的 GenericFastJsonRedisSerializer 或 FastJsonRedisSerializer 作为 RedisTemplate 的 RedisSerializer。下面是配置方式,非常简单。
XML式
如果是使用 XML 的方式配置 Spring Data Redis 的话,只需将 RedisTemplate 中的 Serializer 替换为 GenericFastJsonRedisSerializer 即可。
1
2
3
4
5
6<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory"/>
<property name="defaultSerializer">
<bean class="com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer"/>
</property>
</bean>下面是完整的 Spring 集成 Redis 配置供参考。
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<!-- Redis 连接池配置(可选) -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="${redis.pool.maxActive}"/>
<property name="maxIdle" value="${redis.pool.maxIdle}"/>
<property name="maxWaitMillis" value="${redis.pool.maxWait}"/>
<property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/>
<!-- 更多连接池配置...-->
</bean>
<!-- Redis 连接工厂配置 -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<!--设置连接池配置,不设置的话会使用默认的连接池配置,若想禁用连接池可设置 usePool = false -->
<property name="poolConfig" ref="jedisPoolConfig" />
<property name="hostName" value="${host}"/>
<property name="port" value="${port}"/>
<property name="password" value="${password}"/>
<property name="database" value="${database}"/>
<!-- 更多连接工厂配置...-->
</bean>
<!-- RedisTemplate 配置 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<!-- 设置 Redis 连接工厂-->
<property name="connectionFactory" ref="jedisConnectionFactory"/>
<!-- 设置默认 Serializer ,包含 keySerializer & valueSerializer -->
<property name="defaultSerializer">
<bean class="com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer"/>
</property>
<!-- 单独设置 keySerializer -->
<property name="keySerializer">
<bean class="com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer"/>
</property>
<!-- 单独设置 valueSerializer -->
<property name="valueSerializer">
<bean class="com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer"/>
</property>
</bean>
编程式
如果是使用编程的方式(通常是基于 Spring Boot 项目)配置 RedisTemplate 的话只需在你的配置类(被@Configuration注解修饰的类)中显式创建 RedisTemplate Bean,设置 Serializer 即可。
1
2
3
4
5
6
7
8
9
10
11
12
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
GenericFastJsonRedisSerializer fastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
redisTemplate.setDefaultSerializer(fastJsonRedisSerializer);//设置默认的Serialize,包含 keySerializer & valueSerializer
//redisTemplate.setKeySerializer(fastJsonRedisSerializer);//单独设置keySerializer
//redisTemplate.setValueSerializer(fastJsonRedisSerializer);//单独设置valueSerializer
return redisTemplate;
}通常使用 GenericFastJsonRedisSerializer 即可满足大部分场景
如果你想定义特定类型专用的 RedisTemplate 可以使用 FastJsonRedisSerializer来代替 GenericFastJsonRedisSerializer,配置是类似的。