一文掌握FastJson基础到高级玩法

488 阅读8分钟

基本介绍

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格式的字符串
DisableSingleQuoteDo 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);
    }

}