本文记录FastJson对于序列化及反序列化时实体属性自定解析器实现简述
使用场景
项目在针对实体进行序列化为JSON或者JSON反序列化时,存在自定义字段解析器的需求,最常见的场景为时间属性(标准格式或非标准格式)的序列及反序列化.下文针对该情况,说明如何实现自定时间反序列化解析器.
操作步骤
1.fastjson版本
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.72</version>
</dependency>
2.实现原理及核心类图
对于如何达成FastJson定制序列化,可参考博文Fastjson 定制序列化
FastJson针对反序列化,解析结束后是通过实体中setter方法完成写入,可以通过重写对应属性的set方法改变反序列化结果,但是此种方法不友好,侵入性大,不推荐;
推荐使用@JSONField注解来达成自定反序列化处理器.
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
public @interface JSONField {
/**
* 配置序列化和反序列化的顺序
* config encode/decode ordinal
* @since 1.1.42
* @return
*/
int ordinal() default 0;
/**
* 指定字段的名称
*/
String name() default "";
/**
* 指定字段的格式,对⽇期格式有⽤
*/
String format() default "";
/**
* 属性是否序列化
*/
boolean serialize() default true;
/**
* 属性是否反序列化
*/
boolean deserialize() default true;
/**
* 序列化时可特性选择
*/
SerializerFeature[] serialzeFeatures() default {};
/**
* 反序列化时特性选择
*/
Feature[] parseFeatures() default {};
/**
* 打标记,可定制化输出
*/
String label() default "";
/**
* 当你有一个字段是字符串类型,里面是json格式数据,你希望直接输入,而不是经过转义之后再输出
* @since 1.2.12
*/
boolean jsonDirect() default false;
/**
* 指定序列化使用自定义Serialize
* Serializer class to use for serializing associated value.
*
* @since 1.2.16
*/
Class<?> serializeUsing() default Void.class;
/**
* 指定反序列化使用自定Parser
* Deserializer class to use for deserializing associated value.
*
* @since 1.2.16
*/
Class<?> deserializeUsing() default Void.class;
/**
* 反序列化时,允许多个名字的变量转成一个
* @since 1.2.21
* @return the alternative names of the field when it is deserialized
*/
String[] alternateNames() default {};
/**
* 对象映射到父对象上,不进行子对象映射.简单而言,就是属性为对象的时候,属性对象里的属性直接当做父对象的属性输出
* @since 1.2.31
*/
boolean unwrapped() default false;
/**
* 设置默认值
* Only support Object
*
* @since 1.2.61
*/
String defaultValue() default "";
}
针对本例中对时间属性的处理,可以通过继承com.alibaba.fastjson.serializer.DateCodec并实现cast方法来达成对时间字符串反序列为时间对象的个性化处理.
下述为DateCodec的类图,描述了其在序列化及反序列化过程中重要的接口及方法
2.测试代码
package fastjson;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
import com.alibaba.fastjson.serializer.DateCodec;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Slf4j
public class FastJsonAnnotationTest {
@Data
public static class Commodity{
/**
* 时间,JSON格式字符串为非标准格式时间字符串
*/
@JSONField(format = "yyyy-MM-dd HH:mm:ss",deserializeUsing = LenientStringToDateProcessor.class)
Date createTime;
/**
* 对象属性集合,JSON格式字符串中包含了不同子类
*/
@JSONField(deserializeUsing = PartProcessor.class)
List parts;
}
@Data
public static class HeadFit extends BaseFit{
String head;
}
@Data
public static class BodyFit extends BaseFit{
String body;
}
@Data
public static class BaseFit{
EnumType type;
String creator;
}
public enum EnumType{
head,body
}
@Test
public void jsonFieldTest() {
// 序列化
Commodity commodity = new Commodity();
commodity.setCreateTime(new Date());
List<BaseFit> parts = new ArrayList<>();
HeadFit headFit_01 = new HeadFit();
headFit_01.setHead("Head-01");
headFit_01.setCreator("Head-01");
headFit_01.setType(EnumType.head);
HeadFit headFit_02 = new HeadFit();
headFit_02.setHead("Head-02");
headFit_02.setCreator("Head-02");
headFit_02.setType(EnumType.head);
BodyFit bodyFit = new BodyFit();
bodyFit.setBody("Body-01");
bodyFit.setCreator("Body-01");
bodyFit.setType(EnumType.body);
parts.add(headFit_01);
parts.add(headFit_02);
parts.add(bodyFit);
commodity.setParts(parts);
String jsonString = JSON.toJSONString(commodity);
log.info("序列化结果:{}",jsonString);
// 反序列化
String json = "{\"createTime\":\"2021-1-12 05:12:03\",\"parts\":[{\"creator\":\"Head-01\",\"head\":\"Head-01\",\"type\":\"head\"},{\"creator\":\"Head-02\",\"head\":\"Head-02\",\"type\":\"head\"},{\"body\":\"Body-01\",\"creator\":\"Body-01\",\"type\":\"body\"}]}";
Commodity commodityDest = JSON.parseObject(json, Commodity.class);
log.info("反序列化结果:{}",commodityDest);
HeadFit one = (HeadFit)commodityDest.getParts().get(0);
log.info("反序列化第一条记录:{}",one);
}
public static class LenientStringToDateProcessor extends DateCodec {
@Override
public <T> T cast(DefaultJSONParser parser, Type clazz, Object fieldName, Object val) {
try {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 不要求传入字符串严格符合时间格式
format.setLenient(false);
if (val instanceof String) {
String temp = (String) val;
val = format.parse(temp);
}
} catch (Exception e) {
}
return super.cast(parser, clazz, fieldName, val);
}
}
public static class PartProcessor implements ObjectDeserializer {
/**
* 由于集合序列化后为子类,再进行反序列化时,无法还原原对象,需要修改反序列化方法,手动修改反序列化逻辑
* @param parser
* @param type
* @param fieldName
* @return
*/
@Override
public List<BaseFit> deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
List<BaseFit> results = new ArrayList<>();
List<JSONObject> baseFits = parser.parseArray(JSONObject.class);
for (JSONObject base: baseFits){
/*
* 通过父对象的类型标识字段,选择反序列化方式
*/
EnumType enumType = base.getObject("type", EnumType.class);
if (EnumType.head.equals(enumType)){
HeadFit headFit = base.toJavaObject(HeadFit.class);
results.add(headFit);
}else if(EnumType.body.equals(enumType)){
BodyFit bodyFit = base.toJavaObject(BodyFit.class);
results.add(bodyFit);
}
}
return results;
}
@Override
public int getFastMatchToken() {
return 0;
}
}
}