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());
}
}