基于Jackson实现的动态序列化、反序列化(增强版的@JsonProperty())

723 阅读2分钟

引言

浏览网上关于动态序列化的文章,都不太能满足我想实现的功能,所以在实现该功能后,想分享下。

编写目的

分享一个基于Jackson实现的动态序列化、反序列化的功能,可以理解为一个增强版的@JsonProperty()。

和JsonProperty的区别在于JsonProperty是静态实现,在编码阶段即已经定义好序列化的名字。 而该功能可以在程序运行中实时调整,对key的修改,隐藏等。功能效果

背景

ToB产品,在对接多个上游系统的时候,字段命名差异化怎么解决?比如商品编码:我们使用skuCode,上游使用pluCode,skuId等

于是老大提出一个需求,能不能实现一个对现有系统无侵入,动态序列化反序列化的功能。

功能展示

//举例:一个对象 正常序列化后 
{ "id":1, "name":"auto" }

//A项目中修改配置将name->newName,再次序列化后
 { "id":1, "newName":"auto" }
 
 //B项目中修改配置将name->myName,再次序列化后
 { "id":1, "myName":"auto" }

解决方案

定义需要动态调整的对象

如果你的Api返回下面的Car对象,那么就可以实现上面功能展示的效果,如果需要动态修改的字段使用范围很广,字段抽取为抽象类,让需要动态修改的类继承使用

/**
 * 标记需要做实体字段配置的类
 *
 * <p>该注解支持继承,子类也可以使用</p>
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Inherited
public @interface Metadata {
    String value() default "";
}

//标记要动态序列化的类
@Metadata()
@Data
public class Car {
    private Long id;

    private String name;
}

捕捉动态调整对象并修改key信息

需要继承BeanDeserializerModifierBeanSerializerModifier做自定义配置处理,下面只展示反序列化的配置定义

import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.google.common.collect.Maps;
import com.megvii.lbg.wes.engine.common.annotation.EntityName;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class MyDeserializerModifier extends BeanDeserializerModifier {
    
    private final Map<String,String> cacheMap = Maps.newHashMap();
    
    
    public void addOrUpdateCache(String key, String value) {
        cacheMap.put(key, value);
    }


    

    @Override
    public List<BeanPropertyDefinition> updateProperties(DeserializationConfig config, BeanDescription beanDesc, List<BeanPropertyDefinition> propDefs) {
        Metadata metadata = beanDesc.getBeanClass().getAnnotation(Metadata.class);
        if (metadata == null) {
            return propDefs;
        }

        return propDefs.stream()
                .map(p -> {
                    String fieldName = cacheMap.get(p.getName());
                    return p.withSimpleName(fieldName == null ? p.getName() : fieldName);
                })
                .collect(Collectors.toList());
    }
}

实时修改后,清空Jackson缓存

注意,如果调用上面的addOrUpdateCache方法,Jackson加载数据信息后会缓存,如果在运行中修改,需要删除缓存

SerializerProvider serializerProvider = objectMapper.getSerializerProvider();
if (serializerProvider instanceof DefaultSerializerProvider) {
    //清空缓存,json重命名才能生效
    ((DefaultSerializerProvider) serializerProvider).flushCachedSerializers();
}

参考信息

stackoverflow.com/questions/1…

结束语

这是掘金发表的第一篇分享文章,如果看完觉得有了解到新知识,麻烦点个赞,如果后续万一用得到,可以收藏下,谢谢啦