FastJson对于序列化及反序列化时实体属性自定解析器

本文记录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;
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值