基本介绍
FASTJSON 2是一个国产、性能极致并且简单易用的Java JSON库。
相关依赖
dependencies {
implementation("com.alibaba.fastjson2:fastjson2:2.0.52")
}
基础使用
相关实体
public record Student(String name,Integer age) {
}
public record Invoice(Double price,@JSONField(format = "yyyy-MM-dd") Date date,@JSONField(name = "no") String id) {
}
简单序列化
@Test
public void simpleSerializationTest(){
var stu = new Student("Jonny",12);
String json = JSONObject.toJSONString(stu);
System.out.println(json);
}
简单反序列化
@Test
public void simpleDeserializationTest(){
var json = """
{"age":12,"name":"Jonny"}
""";
var stu = JSONObject.parseObject(json,Student.class);
System.out.println(stu);
}
使用JSONField进行定制化操作
JSONField是作用在Field、Method、Parameter上的Annotation,可以用来指定序列化字段的顺序、名字、格式、是否忽略、配置JSONReader/JSONWriter的Features等。
public void useJsonField(){
var invoice = new Invoice(100D,new Date(),"123456789");
var json = JSONObject.toJSONString(invoice);
System.out.println(json);
}
结果:
{"date":"2024-10-23","no":"123456789","price":100.0}
深度解析功能
设置序列化、反序列化的别名
可以放置到字段上
public class A {
@JSONField(name = "ID")
public int id;
}
也可以放置到getter、setter方法上
public class A {
private int id;
@JSONField(name = "ID")
public int getId() {return id;}
@JSONField(name = "ID")
public void setId(int value) {this.id = id;}
}
定制反序列化的格式——时间
public class VO {
// 配置date序列化和反序列使用yyyyMMdd日期格式
@JSONField(format = "yyyyMMdd")
public Date date;
}
设置序列化、反序列化是否需要忽略某个字段
public class VO {
@JSONField(serialize = false) // 不用序列化
public Date date1;
@JSONField(deserialize = false) // 不用反序列化
public Date date1;
}
指定字段序列化后的顺序
public static class VO {
@JSONField(ordinal = 1)
public String type;
@JSONField(ordinal = 2)
public String templateId;
}
指定序列化时的特殊策略-序列化时按照字段的顺序输出、非空才输出
@Test
public void test() {
Bean bean = new Bean();
bean.id = 100;
assertEquals("{"id":"100"}", JSON.toJSONString(bean));
}
public static class Bean {
@JSONField(serializeFeatures = Feature.WriteNonStringValueAsString)
public int id;
}
通过JSONField(value = true)配置JavaBean序列化字段(适用于枚举)和反序列化构造方式
将@JSONField(value = true)配置在其中一个字段上,序列化时,会将对象序列化时按照该字段的值输出。将@JSONField(value = true)配置在构造函数参数上,而且该构造函数只有1个参数,就会按照这个参数来构造对象
public static class Bean2 {
private final int code;
public Bean2(@JSONField(value = true) int code) {
this.code = code;
}
@JSONField (value = true)
public int getCode() {
return code;
}
}
@Test
public void test2() {
Bean2 bean = new Bean2(101);
String str = JSON.toJSONString(bean);
assertEquals("101", str);
Bean2 bean1 = JSON.parseObject(str, Bean2.class);
assertEquals(bean.code, bean1.code);
}
通过JSONType配置序列化和反序列化时忽略某些字段
@JSONType(ignores = {"id2", "id3"})
public static class Bean {
public int getId() {
return 101;
}
public int getId2() {
return 102;
}
public int getId3() {
return 103;
}
}
通过JSONType配置序列化时保持原生类字段顺序
@Slf4j
public class JSONTypeAlphabetic {
@JSONType(alphabetic = false)
public static class Bean {
public int f3;
public int f1;
public int f2;
public int f0;
}
@Test
public void test() {
Bean bean = new Bean();
bean.f0 = 101;
bean.f1 = 102;
bean.f2 = 103;
bean.f3 = 104;
log.info(JSON.toJSONString(bean));
//{"f3":104,"f1":102,"f2":103,"f0":101}
}
}
通过tJSONType 配置序列化时的JSONReader/JSONWriter的Features
@Slf4j
public class JSONTypeFeatures {
// 反序列化时对字符串进行trim
// 序列化时输出为null的字段
@JSONType(deserializeFeatures = JSONReader.Feature.TrimString, serializeFeatures = JSONWriter.Feature.WriteNulls)
public static class OrdersBean {
public String filed1;
public String filed2;
}
@Test
public void test() {
OrdersBean bean = new OrdersBean();
bean.filed1 = "fastjson2";
log.info(JSON.toJSONString(bean));
//{"filed1":"fastjson2","filed2":null}
String json="{"filed1":" fastjson2 ","filed2":"2"}";
OrdersBean bean2 = JSON.parseObject(json, OrdersBean.class);
log.info(bean2.filed1);
//fastjson2
}
}
通过JSONType配置序列化时字段顺序
@Slf4j
public class JSONTypeOrders {
@JSONType(orders = {"filed4", "filed3", "filed2", "filed1"})
public static class OrdersBean {
public String filed1;
public String filed2;
public String filed3;
public String filed4;
}
@Test
public void test() {
OrdersBean bean = new OrdersBean();
bean.filed1 = "1";
bean.filed2 = "2";
bean.filed3 = "3";
bean.filed4 = "4";
log.info(JSON.toJSONString(bean));
//{"filed4":"4","filed3":"3","filed2":"2","filed1":"1"}
}
}
通过ValueFilter实现对序列化后的值二次处理
public interface ValueFilter
extends Filter {
Object apply(Object object, String name, Object value);
}
@Test
public void test_valuefilterCompose() {
ValueFilter filter0 = (source, name, value) -> {
if ("id".equals(name)) {
return ((Integer) value).intValue() + 1;
}
return value;
};
ValueFilter filter1 = (source, name, value) -> {
if ("id".equals(name)) {
return ((Integer) value).intValue() + 10;
}
return value;
};
Bean bean = new Bean();
bean.id = 100;
String str = JSON.toJSONString(bean, ValueFilter.compose(filter0, filter1));
assertEquals("{"id":111}", str);
}
Features介绍
反序列化时,支持的Feature有:
JSONReader.Feature | 介绍 |
---|---|
FieldBased | 基于字段反序列化,如果不配置,会默认基于public的field和getter方法序列化。配置后,会基于非static的field(包括private)做反序列化。在fieldbase配置下会更安全 |
IgnoreNoneSerializable | 反序列化忽略非Serializable类型的字段 |
SupportArrayToBean | 支持数据映射的方式 |
InitStringFieldAsEmpty | 初始化String字段为空字符串"" |
SupportAutoType | 支持自动类型,要读取带"@type"类型信息的JSON数据,需要显式打开SupportAutoType |
SupportSmartMatch | 默认下是camel case精确匹配,打开这个后,能够智能识别camel/upper/pascal/snake/Kebab五中case |
UseNativeObject | 默认是使用JSONObject和JSONArray,配置后会使用LinkedHashMap和ArrayList |
SupportClassForName | 支持类型为Class的字段,使用Class.forName。为了安全这个是默认关闭的 |
IgnoreSetNullValue | 忽略输入为null的字段 |
UseDefaultConstructorAsPossible | 尽可能使用缺省构造函数,在fieldBase打开这个选项没打开的时候,会可能用Unsafe.allocateInstance来实现 |
UseBigDecimalForFloats | 默认配置会使用BigDecimal来parse小数,打开后会使用Float |
UseBigDecimalForDoubles | 默认配置会使用BigDecimal来parse小数,打开后会使用Double |
ErrorOnEnumNotMatch | 默认Enum的name不匹配时会忽略,打开后不匹配会抛异常 |
TrimString | 对读取到的字符串值做trim处理 |
ErrorOnNotSupportAutoType | 遇到AutoType报错(缺省是忽略) |
DuplicateKeyValueAsArray | 重复Key的Value不是替换而是组合成数组 |
AllowUnQuotedFieldNames | 支持不带双引号的字段名 |
NonStringKeyAsString | 非String类型的Key当做String处理 |
Base64StringAsByteArray | 将byte[]序列化为Base64格式的字符串 |
DisableSingleQuote | Do not allow single quote on key and value |
序列化时,支持的Feature有:
JSONWriter.Feature | 介绍 |
---|---|
FieldBased | 基于字段序列化,如果不配置,会默认基于public的field和getter方法序列化。配置后,会基于非static的field(包括private)做序列化。 |
IgnoreNoneSerializable | 序列化忽略非Serializable类型的字段 |
BeanToArray | 将对象序列为[101,"XX"]这样的数组格式,这样的格式会更小 |
WriteNulls | 序列化输出空值字段 |
BrowserCompatible | 在大范围超过JavaScript支持的整数,输出为字符串格式 |
NullAsDefaultValue | 将空置输出为缺省值,Number类型的null都输出为0,String类型的null输出为"",数组和Collection类型的输出为[] |
WriteBooleanAsNumber | 将true输出为1,false输出为0 |
WriteNonStringValueAsString | 将非String类型的值输出为String,不包括对象和数据类型 |
WriteClassName | 序列化时输出类型信息 |
NotWriteRootClassName | 打开WriteClassName的同时,不输出根对象的类型信息 |
NotWriteHashMapArrayListClassName | 打开WriteClassName的同时,不输出类型为HashMap/ArrayList类型对象的类型信息,反序列结合UseNativeObject使用,能节省序列化结果的大小 |
NotWriteDefaultValue | 当字段的值为缺省值时,不输出,这个能节省序列化后结果的大小 |
WriteEnumsUsingName | 序列化enum使用name |
WriteEnumUsingToString | 序列化enum使用toString方法 |
IgnoreErrorGetter | 忽略setter方法的错误 |
PrettyFormat | 格式化输出 |
ReferenceDetection | 打开引用检测,这个缺省是关闭的,和fastjson 1.x不一致 |
WriteNameAsSymbol | 将字段名按照symbol输出,这个仅在JSONB下起作用 |
WriteBigDecimalAsPlain | 序列化BigDecimal使用toPlainString,避免科学计数法 |
UseSingleQuotes | 使用单引号 |
MapSortField | 对Map中的KeyValue按照Key做排序后再输出。在有些验签的场景需要使用这个Feature |
WriteNullListAsEmpty | 将List类型字段的空值序列化输出为空数组"[]" |
WriteNullStringAsEmpty | 将String类型字段的空值序列化输出为空字符串"" |
WriteNullNumberAsZero | 将Number类型字段的空值序列化输出为0 |
WriteNullBooleanAsFalse | 将Boolean类型字段的空值序列化输出为false |
NotWriteEmptyArray | 数组类型字段当length为0时不输出 |
WriteNonStringKeyAsString | 将Map中的非String类型的Key当做String类型输出 |
ErrorOnNoneSerializable | 序列化非Serializable对象时报错 |
WritePairAsJavaBean | 将 Apache Common 包中的Pair对象当做JavaBean序列化 |
BrowserSecure | 浏览器安全,将会'<' '>' '(' ')'字符做转义输出 |
WriteLongAsString | 将Long序列化为String |
WriteEnumUsingOrdinal | 序列化Enum使用Ordinal,缺省是name |
WriteThrowableClassName | 序列化Throwable时带上类型信息 |
LargeObject | 这个是一个保护措施,是为了防止序列化有循环引用对象消耗过大资源的保护措施。 |
UnquoteFieldName | 不带引号输出Key |
NotWriteSetClassName | 当打开WriteClassName时又不想输出Set的类型信息,使用这个Feature |
NotWriteNumberClassName | 当打开WriteClassName时又不想输出Number的类型信息,比如L/S/B/F/D这种后缀,使用这个Feature |
自定义序列化、反序列化器
通过实现ObjectWriter和ObjectReader来实现自定义某个类型的序列化、反序列化。 接口定义:
public interface ObjectWriter<T> {
void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features);
}
public interface ObjectReader<T> {
T readObject(JSONReader jsonReader, Type fieldType, Object fieldName, long features);
}
自定义:
class InstantReader implements ObjectReader {
public static final InstantReader INSTANCE = new InstantReader();
@Override
public Object readObject(JSONReader jsonReader, Type fieldType, Object fieldName, long features) {
if (jsonReader.nextIfNull()) {
return null;
}
long millis = jsonReader.readInt64Value();
return Instant.ofEpochMilli(millis);
}
}
进行注册:
JSON.register(Instant.class, ObjectReader.INSTANCE);
支持JsonPath
在FASTJSON2中,JSONPath是一等公民,支持通过JSONPath在不完整解析JSON Document的情况下,根据JSONPath读取内容;也支持用JSONPath对JavaBean求值,可以在Java框架中当做对象查询语言(OQL)来使用
public void test_entity() throws Exception {
Entity entity = new Entity(123, new Object());
assertSame(entity.getValue(), JSONPath.eval(entity, "$.value"));
assertTrue(JSONPath.contains(entity, "$.value"));
assertEquals(2, JSONPath.size(entity, "$"));
assertEquals(0, JSONPath.size(new Object[], "$"));
}
public static class Entity {
private Integer id;
private String name;
private Object value;
public Entity() {}
public Entity(Integer id, Object value) { this.id = id; this.value = value; }
public Entity(Integer id, String name) { this.id = id; this.name = name; }
public Entity(String name) { this.name = name; }
public Integer getId() { return id; }
public Object getValue() { return value; }
public String getName() { return name; }
public void setId(Integer id) { this.id = id; }
public void setName(String name) { this.name = name; }
public void setValue(Object value) { this.value = value; }
}
与Spring进行集成(不建议)
依赖
dependencies {
implementation 'com.alibaba.fastjson2:fastjson2-extension-spring6:2.0.52'
}
配置:
@Configuration
@EnableWebMvc
public class CustomWebMvcConfigurer implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
//自定义配置...
FastJsonConfig config = new FastJsonConfig();
config.setDateFormat("yyyy-MM-dd HH:mm:ss");
config.setReaderFeatures(JSONReader.Feature.FieldBased, JSONReader.Feature.SupportArrayToBean);
config.setWriterFeatures(JSONWriter.Feature.WriteMapNullValue, JSONWriter.Feature.PrettyFormat);
converter.setFastJsonConfig(config);
converter.setDefaultCharset(StandardCharsets.UTF_8);
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_JSON));
converters.add(0, converter);
}
}