简介

  • 对于 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
    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {

    /**
    * ⾃定义JSON转换器
    *
    * @param converters
    */
    @Override
    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.toJSONStringparseObject

    1
    2
    3
    4
    5
    6
    7
    package 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
    3
    import 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
    22
    public 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
    2
    JSON.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
    18
    package com.alibaba.fastjson.annotation;

    public @interface 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
      7
      public class VO {
      @JSONField(name="ID")
      private int id;

      @JSONField(name="birthday",format="yyyy-MM-dd")
      public Date date;
      }
    • 配置在 Getter/Setter 上

      1
      2
      3
      4
      5
      6
      7
      8
      9
      public class VO {
      private int id;

      @JSONField(name="ID")
      public int getId() { return id;}

      @JSONField(name="ID")
      public void setId(int id) {this.id = id;}
      }
  • 使用format配置日期格式化

    • 可以定制化配置各个日期字段的格式化

      1
      2
      3
      4
      5
      public class A {
      // 配置date序列化和反序列使用yyyyMMdd日期格式
      @JSONField(format="yyyyMMdd")
      public Date date;
      }
  • 使用serialize/deserialize指定字段不序列化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class A {
    @JSONField(serialize=false)
    public Date date;
    }

    public class A {
    @JSONField(deserialize=false)
    public Date date;
    }
  • 使用ordinal指定字段的顺序

    • 缺省Fastjson序列化一个java bean,是根据fieldName的字母序进行序列化的,你可以通过ordinal指定字段的顺序。这个特性需要1.1.42以上版本。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public static class VO {
    @JSONField(ordinal = 3)
    private int f0;

    @JSONField(ordinal = 2)
    private int f1;

    @JSONField(ordinal = 1)
    private int f2;
    }
  • 使用serializeUsing制定属性的序列化类

    • 在fastjson 1.2.16版本之后,JSONField支持新的定制化配置serializeUsing,可以单独对某一个类的某个属性定制序列化,比如:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      public static class Model {
      @JSONField(serializeUsing = ModelValueSerializer.class)
      public int value;
      }

      public static class ModelValueSerializer implements ObjectSerializer {
      @Override
      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
      4
      Model 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
    3
    public interface PropertyFilter extends SerializeFilter {
    boolean apply(Object object, String propertyName, Object propertyValue);
    }
  • 可以通过扩展实现根据object或者属性名称或者属性值进行判断是否需要序列化。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    PropertyFilter 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); // 序列化的时候传入filter
  • PropertyPreFilter 根据PropertyName判断是否序列化

    • 和PropertyFilter不同只根据object和name进行判断,在调用getter之前,这样避免了getter调用可能存在的异常。

      1
      2
      3
      public interface PropertyPreFilter extends SerializeFilter {
      boolean apply(JSONSerializer serializer, Object object, String name);
      }
  • NameFilter 序列化时修改Key

    • 如果需要修改Key,process返回值则可

      1
      2
      3
      public interface NameFilter extends SerializeFilter {
      String process(Object object, String propertyName, Object propertyValue);
      }
  • fastjson内置一个PascalNameFilter,用于输出将首字符大写的Pascal风格。例如:

    1
    2
    3
    4
    import com.alibaba.fastjson.serializer.PascalNameFilter;

    Object obj = ...;
    String jsonStr = JSON.toJSONString(obj, new PascalNameFilter());
  • ValueFilter 序列化时修改Value

    1
    2
    3
    public interface ValueFilter extends SerializeFilter {
    Object process(Object object, String propertyName, Object propertyValue);
    }
  • BeforeFilter 序列化时在最前添加内容

    • 在序列化对象的所有属性之前执行某些操作,例如调用 writeKeyValue 添加内容

      1
      2
      3
      4
      5
      public 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
      5
      public 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
    18
    public 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
    27
    public 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
    @Bean
    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,配置是类似的。