Jackson2.x自定义枚举值转换

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

public abstract class DeserializationContext extends DatabindContext implements Serializable {
    ... 
    protected LinkedNode _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 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

你可能感兴趣的:(Java)