抽象类使用Jackson序列化问题
追梦菜鸟 人气:0抽象类使用Jackson序列化
当java对象中含List<Object>时,如果Object一个抽象类或接口,这里就会出现java多态的现象,比如List<Animal>, 如果Animal是个抽象类,并且有多个子类时,由于List中保存的Animal没有明确指向具体的子类或实现类,json反序列化java对象时就会抛出提示:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException:Can not construct instance of Animal, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
可以使用@JsonTypeInfo与@JsonSubTypes来解决此类问题,通过注解,可以在序列化时,保存具体的类型信息到json中,当json反序列到java对象时,就可以根据具体类型信息创建正确的java对象。
@JsonTypeInfo
– indicates details of what type information to include in serialization 指出序列化包含的类型信息细节@JsonSubTypes
– indicates sub-types of the annotated type 指出被注解类型的子类@JsonTypeName
– defines a logical type name to use for annotated class 定义被注解类使用的逻辑名称
@JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = As.PROPERTY, property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = Dog.class, name = "dog"), @JsonSubTypes.Type(value = Cat.class, name = "cat") }) public class Animal { public String name; public Animal(String name) { } } @JsonTypeName("dog") // 这里在子类中指定的type name必须和抽象类中注解@JsonSubTypes中name属性指定的值保持一致 public class Dog extends Animal { public double barkVolume; public Dog(String name) { super(name); barkVolume = 0.5; } } @JsonTypeName("cat") public class Cat extends Animal { boolean likesCream; public int lives; public Cat(String name) { super(name); likesCream = true; lives = 10; } } @Test public void whenSerializingPolymorphic_thenCorrect() throws JsonProcessingException { Zoo.Dog dog = new Zoo.Dog("lacy"); Zoo zoo = new Zoo(dog); String result = new ObjectMapper() .writeValueAsString(zoo); assertThat(result, containsString("type")); assertThat(result, containsString("dog")); } 序列化zoo对象,结果如下: { "type":"dog", "name":"lacy", "barkVolume":0 } @Test public void whenDeserializingPolymorphic_thenCorrect() throws IOException { String json = "{\"name\":\"lacy\",\"type\":\"cat\"}"; Animal animal = new ObjectMapper().readerFor(Animal.class).readValue(json); assertEquals("lacy", animal.name); assertEquals(Cat.class, animal.getClass()); }
记一次jackson序列化Boolean的坑
@Data public class CouponTemplateDto { /** * 优惠券类型id */ private Long couponTypeId; /** * 优惠券模板id */ private Long couponTemplateId; /** * 用户id */ private Long userId; /** * 优惠券描述 */ private String description; /** * 面值,满200减30,则此值为30 */ private BigDecimal value; /** * 从次日起,多少天可用 */ private Integer delayDays; /** * 从当日起,多少天可用 */ private Integer nowDays; /** * 满多少可以减,满200减30,则此值为200 */ private BigDecimal fullAmount; /** * 券号 */ private String couponNo; /** * 有效起始日期 */ private Date startTime; /** * 失效日期 */ private Date endTime; /** * 创建时间 */ private Date createTime; /** * 使用日期 */ private Date useTime; /** * 券使用状态:0-未使用 1-已使用 2-已过期 */ private Integer couponUseStatus; /** * 过期前多少天提醒,默认7天 */ private Integer overDueRemind; /** * 优惠券标题 */ private String title; /** * 优惠券是否能开始使用 */ // @JsonProperty("isStart") private Boolean start; /** * 优惠券是否过期 */ // @JsonProperty("isEnd") private Boolean end; private Boolean getStart() { return startTime.before(new Date()); } private Boolean getEnd() { return endTime.before(new Date()); } }
我定义了一个这样的类,我们项目用的是Spring Boot,默认底层采用的是jackson序列化,但是在使用中出了一个问题private Boolean start跟private Boolean end这两个字段一直无法序列化
总结排查思路如下
1.是boolean还是Boolean,到底是基本数据类型还是包装类,如果是基本数据类型的话(包装类可以使用,但是不推荐),不要使用is开头。我们可以看看阿里巴巴规范中的这段话
【强制】POJO类中的任何布尔类型的变量,都不要加 is,否则部分框架解析会引起序列化错误。
反例:定义为基本数据类型 boolean isSuccess;的属性,它的方法也是 isSuccess(),RPC框架在反向解析的时候,“以为”对应的属性名称是 success,导致属性获取不到,进而抛出异常。
2.这个错误也是我犯的错误,我复写了get方法,方法的访问权限被设置成了private级别
解决方案:
- 加注解,@JsonProperty(“isEnd”)
- 将方法级别更正为public
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。
加载全部内容