欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Jackson2.x自定义枚举值转换

程序员文章站 2022-04-13 21:36:54
...

Java原生Enum太难用,对Enum进行扩展,添加了value属性,name属性;在用Jackson进行反序列化的时候遇到转换问题,简单的做法就是创建扩展枚举类转换器,在需要进行转换的属性上添加@JsonDeserialize注解。 这种做法有两个痛点:
​ 1.针对每个扩展的枚举类,都需要制定一个转换器。
​ 2.每个需要转换的枚举属性上都需要添加注解;
是否可以创建一个通用的扩展枚举类转换器,处理所有的枚举类呢。这里遇到本文难点,如何获取属性的类对象。很自然的想从DeserializationContext中获取,如下:

public abstract class DeserializationContext extends DatabindContext implements Serializable {
    ... 
    protected LinkedNode<JavaType> _currentType;
    ...
    public JavaType getContextualType() {
        return this._currentType == null ? null : (JavaType)this._currentType.value();
    }

​ 一开始尝试ctx.getContextualType().getRawClass()获取,但调用该方法一定会NullPointException;跟踪jackson源码后发现,发现ctx构造函数里里压根就不会初始化_currentType字段;不初始化放这里干嘛,一定别的用途,在DeserializationContext 发现有如下两个函数会给_currentType赋值。

public JsonDeserializer<?> handlePrimaryContextualization(JsonDeserializer<?> deser, BeanProperty prop, JavaType type) throws JsonMappingException {
    if (deser instanceof ContextualDeserializer) {
        this._currentType = new LinkedNode(type, this._currentType);
        try {
            deser = ((ContextualDeserializer)deser).createContextual(this, prop);
        } finally {
            this._currentType = this._currentType.next();
        }
    }
    return deser;
}

public JsonDeserializer<?> handleSecondaryContextualization(JsonDeserializer<?> deser, BeanProperty prop, JavaType type) throws JsonMappingException {
    if (deser instanceof ContextualDeserializer) {
        this._currentType = new LinkedNode(type, this._currentType);
        try {
            deser = ((ContextualDeserializer)deser).createContextual(this, prop);
        } finally {
            this._currentType = this._currentType.next();
        }
    }
    return deser;
}

检查ContextualDeserializer,这里是一个接口,那只需要我的CommonDeserializer 实现这个接口就好了啊,

public interface ContextualDeserializer {
    JsonDeserializer<?> createContextual(DeserializationContext var1, BeanProperty var2) throws JsonMappingException;
}

最终得到的CommonDeserializer

@Data
public class CommonDeserializer extends JsonDeserializer<Enum> implements ContextualDeserializer {
    private Class clz;
    @Override
    public Enum deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException {
        if(StringUtils.isEmpty(jsonParser.getText())){
            return null;
        }
        if(BaseEnumUtil.isImplementsBaseEnum(clz)){
            return  BaseEnumUtil.convertToEnum(clz,jsonParser.getIntValue());
        }else {
            return null;
        }
    }

    /**
     * 获取合适的解析器,把当前解析的属性Class对象存起来,以便反序列化的转换类型,为了避免线程安全问题,每次都new一个(通过threadLocal来存储更合理)
     * @param ctx
     * @param property
     * @return
     * @throws JsonMappingException
     */
    public JsonDeserializer createContextual(DeserializationContext ctx, BeanProperty property) throws JsonMappingException {
        Class rawCls = ctx.getContextualType().getRawClass();
        CommonDeserializer clone = new CommonDeserializer();
        clone.setClz(rawCls);
        return clone;
    }
}

扩展Jackson ObjectMapper,将通用枚举转换器注入进去,解决第二个痛点;

@Slf4j
public class OrderEnumConvertTest {
    private ObjectMapper objectMapper;
    @Before
    public void init(){
        objectMapper = new ObjectMapper();
        SimpleModule simpleModule= new SimpleModule();
        simpleModule.addDeserializer(Enum.class,new CommonDeserializer());
        objectMapper.registerModule(simpleModule);
    }

    @Test
    public  void object2Json()throws Exception{
        OrderDto orderDto = new OrderDto();
        orderDto.setOrderType(OrderEnum.JD);
        String json = objectMapper.writeValueAsString(orderDto);
        log.info(json);
        String jsonExp ="{\"orderType\":20}";
        Assert.assertEquals(jsonExp,json);
    }

    @Test
    public  void jsonToObject()throws Exception{
       String json = "{\"orderType\":20}";
        OrderDto orderDto =(OrderDto)objectMapper.readValue(json,OrderDto.class);
        Assert.assertEquals(OrderEnum.JD,orderDto.getOrderType());
    }
}	

源码链接todo

相关标签: Jackson