Fastjson1 特性配置 SerializerFeature

这里介绍Fastjson1中一个极为强大的功能 SerializerFeature。如果你在序列化JSON时总遇到日期格式不对、字段缺失、中文乱码等问题,SerializerFeature就可以解决你的问题。

SerializerFeature是什么?

简单来说,SerializerFeature是Fastjson提供的序列化特性配置,它允许你通过枚举常量控制JSON输出的各种细节。就像给JSON输出加上滤镜,让你精准控制最终结果。

核心特性速览

以下是最常用的几个Feature(文末附完整列表):

Feature功能说明
WriteMapNullValue输出值为null的字段
WriteNullStringAsEmptynull字符串转为空字符串""
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 会引入安全风险(反序列化漏洞),只在可信数据环境下使用。

常见问题与避坑指南

  1. 多个Feature组合:直接传入变长参数,用逗号分隔即可
  2. 优先级问题WriteNullStringAsEmpty必须配合WriteMapNullValue使用
  3. 全局配置:可通过SerializerFeature.config(JSON.DEFAULT_GENERATE_FEATURE, feature, true)全局启用

全量Feature速查表

Feature说明
QuoteFieldNames输出字段名带双引号(默认)
UseSingleQuotes使用单引号替代双引号
SortField按字母排序字段
WriteEnumUsingToString使用枚举的toString()
SkipTransientField跳过transient字段
WriteClassName写入类名(反序列化用)

总结

掌握SerializerFeature,可以灵活使用Fastjons1的JSON序列化。建议在开发时按需组合,比如生产环境关闭PrettyFormat,调试时开启。