Fastjson1 特性配置 SerializerFeature
这里介绍Fastjson1中一个极为强大的功能 SerializerFeature。如果你在序列化JSON时总遇到日期格式不对、字段缺失、中文乱码等问题,SerializerFeature就可以解决你的问题。
SerializerFeature是什么?
简单来说,SerializerFeature是Fastjson提供的序列化特性配置,它允许你通过枚举常量控制JSON输出的各种细节。就像给JSON输出加上滤镜,让你精准控制最终结果。
核心特性速览
以下是最常用的几个Feature(文末附完整列表):
| Feature | 功能说明 |
|---|---|
WriteMapNullValue | 输出值为null的字段 |
WriteNullStringAsEmpty | null字符串转为空字符串"" |
WriteDateUseDateFormat | 使用指定格式输出日期 |
PrettyFormat | 格式化输出(换行缩进) |
DisableCircularReferenceDetect | 禁用循环引用检测(提升性能) |
Feature示例
输出值为 null 的字段
单独示例
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
public TestWriteMapNullValue {
public static void main(String[] args) {
User user = new User("小明", null, null);
// 默认不输出 null 字段
System.out.println("默认:" + JSON.toJSONString(user));
// 启用 WriteMapNullValue
System.out.println("启用后:" + JSON.toJSONString(user, SerializerFeature.WriteMapNullValue));
}
}
// 输出:
// 默认:{"name":"小明"}
// 启用后:{"age":null,"email":null,"name":"小明"}
null 字符串转为空串 ""
注意:必须配合
WriteMapNullValue才有效。
public TestWriteNullStringAsEmpty {
public static void main(String[] args) {
User user = new User("小明", null, null);
String json = JSON.toJSONString(user,
SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteNullStringAsEmpty);
System.out.println(json);
// 输出:{"age":null,"email":"","name":"小明"}
}
}
使用全局日期格式
import java.util.Date;
public TestWriteDateUseDateFormat {
public static void main(String[] args) {
Order order = new Order();
order.setCreateTime(new Date());
// 单独使用:会按照 yyyy-MM-dd HH:mm:ss 格式输出
String json = JSON.toJSONString(order, SerializerFeature.WriteDateUseDateFormat);
System.out.println("单独使用:" + json);
// 自定义全局格式
JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd";
String json2 = JSON.toJSONString(order, SerializerFeature.WriteDateUseDateFormat);
System.out.println("自定义格式:" + json2);
}
}
// 输出:
// 单独使用:{"createTime":"2026-05-12 14:30:00"}
// 自定义格式:{"createTime":"2026-05-12"}
使用注解的方式
@JSONField(format = "yyyy-MM-dd")
private Date birthday;
美化输出(换行缩进)
public TestPrettyFormat {
public static void main(String[] args) {
User user = new User("小红", 25, "[email protected]");
String prettyJson = JSON.toJSONString(user, SerializerFeature.PrettyFormat);
System.out.println(prettyJson);
}
}
// 输出:
// {
// "age":25,
// "email":"[email protected]",
// "name":"小红"
// }
禁用循环引用检测
import com.alibaba.fastjson.JSON;
public TestDisableCircularReferenceDetect {
public static void main(String[] args) {
User user = new User("张三", 30, "[email protected]");
// 默认会检测循环引用但本场景无循环,增加额外开销
long start1 = System.nanoTime();
for (int i = 0; i < 100000; i++) {
JSON.toJSONString(user);
}
long cost1 = System.nanoTime() - start1;
long start2 = System.nanoTime();
for (int i = 0; i < 100000; i++) {
JSON.toJSONString(user, SerializerFeature.DisableCircularReferenceDetect);
}
long cost2 = System.nanoTime() - start2;
System.out.println("默认模式耗时:" + cost1);
System.out.println("禁用检测耗时:" + cost2);
// 典型输出(禁用后更快):
// 默认模式耗时:83147501
// 禁用检测耗时:13533800 (显著减少)
}
}
字段名输出双引号
默认就是 true,一般无需显式指定
如果设置 QuoteFieldNames=false,字段名将不带双引号(非标准 JSON,但某些场景可用)。
User user = new User("李四", 28, "[email protected]");
// 默认开启双引号
System.out.println("默认输出: " + JSON.toJSONString(user));
// 输出: {"age":28,"email":"[email protected]","name":"李四"}
// 方案1:通过 SerializeWriter 直接控制
SerializeWriter writer = new SerializeWriter();
writer.config(SerializerFeature.QuoteFieldNames, false); // 这里 config 是 SerializeWriter 的方法
JSONSerializer serializer = new JSONSerializer(writer);
serializer.write(user);
System.out.println("关闭双引号: " + writer.toString());
// 输出: {age:28,email:"[email protected]",name:"李四"}
// 注意:值仍然带双引号,只有字段名不带
// 方案2 使用单引号替代双引号(这是官方支持的方式)
String json = JSON.toJSONString(user, SerializerFeature.UseSingleQuotes);
System.out.println("单引号模式: " + json);
// 输出: {'age':28,'email':'[email protected]','name':'李四'}
使用单引号代替双引号
public TestUseSingleQuotes {
public static void main(String[] args) {
User user = new User("王芳", 22, "[email protected]");
String json = JSON.toJSONString(user, SerializerFeature.UseSingleQuotes);
System.out.println(json);
// 输出:{'age':22,'email':'[email protected]','name':'王芳'}
}
}
按字段名字母排序输出
public TestSortField {
public static void main(String[] args) {
User user = new User("赵雷", 35, "[email protected]");
String sortedJson = JSON.toJSONString(user, SerializerFeature.SortField);
System.out.println("未排序:" + JSON.toJSONString(user));
System.out.println("排序后:" + sortedJson);
}
}
// 输出:
// 未排序:{"age":35,"email":"[email protected]","name":"赵雷"}
// 排序后:{"age":35,"email":"[email protected]","name":"赵雷"}
Fastjson 1.x 中 SortField 默认就是开启的,所以大多数情况下你不需要显式指定它。只有在通过 SerializeConfig 关闭排序后,才能看出区别。
使用枚举的 toString()
enum Status {
SUCCESS, FAIL;
@Override
public String toString() {
return this == SUCCESS ? "成功" : "失败";
}
}
class Data {
private Status status;
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
}
public class TestWriteEnumUsingToString {
public static void main(String[] args) {
Data d = new Data();
d.setStatus(Status.SUCCESS);
// 默认使用枚举的 name()
System.out.println("默认(name): " + JSON.toJSONString(d));
// 输出: {"status":"SUCCESS"}
// 使用枚举的 toString()
System.out.println("使用toString: " + JSON.toJSONString(d, SerializerFeature.WriteEnumUsingToString));
// 输出: {"status":"成功"}
}
}
跳过 transient 字段
import java.io.Serializable; Account implements Serializable {
public String username;
public transient String password; // transient 字段默认不序列化
}
public TestSkipTransientField {
public static void main(String[] args) {
Account acc = new Account();
acc.username = "admin";
acc.password = "123456";
// 默认已经跳过 transient
System.out.println(JSON.toJSONString(acc));
// 即使强制要求输出 transient 字段(需要额外配置),SkipTransientField 就是默认行为
// 这里展示显式使用该 Feature(效果与默认相同)
System.out.println(JSON.toJSONString(acc, SerializerFeature.SkipTransientField));
}
}
// 输出两次都是:{"username":"admin"} 不会输出 password
写入类名信息
用于反序列化时还原具体类型
abstract Animal {
public String;
} Dog extends Animal {
public String bark;
} Cat extends Animal {
public String meow;
}
public TestWriteClassName {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "旺财";
dog.bark = "汪汪";
// 不写入类名
System.out.println(JSON.toJSONString(dog));
// 写入类名(输出 @type 字段)
String jsonWithType = JSON.toJSONString(dog, SerializerFeature.WriteClassName);
System.out.println(jsonWithType);
// 反序列化时可还原为 Dog 对象
Animal animal = (Animal) JSON.parse(jsonWithType);
System.out.println(animal.getClass().getSimpleName()); // 输出 Dog
}
}
// 输出:
// {"bark":"汪汪","name":"旺财"}
// {"@type":"Dog","bark":"汪汪","name":"旺财"}
// Dog
组合使用与避坑提醒
WriteNullStringAsEmpty必须与WriteMapNullValue同时出现,否则无效。PrettyFormat仅用于调试,生产环境建议关闭以节省带宽。DisableCircularReferenceDetect在确定无循环引用时可显著提升性能。WriteClassName会引入安全风险(反序列化漏洞),只在可信数据环境下使用。
常见问题与避坑指南
- 多个Feature组合:直接传入变长参数,用逗号分隔即可
- 优先级问题:
WriteNullStringAsEmpty必须配合WriteMapNullValue使用 - 全局配置:可通过
SerializerFeature.config(JSON.DEFAULT_GENERATE_FEATURE, feature, true)全局启用
全量Feature速查表
| Feature | 说明 |
|---|---|
| QuoteFieldNames | 输出字段名带双引号(默认) |
| UseSingleQuotes | 使用单引号替代双引号 |
| SortField | 按字母排序字段 |
| WriteEnumUsingToString | 使用枚举的toString() |
| SkipTransientField | 跳过transient字段 |
| WriteClassName | 写入类名(反序列化用) |
总结
掌握SerializerFeature,可以灵活使用Fastjons1的JSON序列化。建议在开发时按需组合,比如生产环境关闭PrettyFormat,调试时开启。

