文章目录
- 1、正常反序列化的过程
- 2、@JsonCreator
- 3、@JsonValue
- 4、应用:枚举类中校验传参以及优化前后端数据交互
- 5、补充:@ConstructorProperties
1、正常反序列化的过程
反序列化时,默认会调用实体类的无参构造来实例化一个对象,然后使用setter来初始化属性值。写点测试代码:
@AllArgsConstructor @Data public class Book { private String name; private Double price; }
复制
测试下:
public class TestSome1 { @Test void testJson() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); Book book = objectMapper.readValue("{\n" + " \"name\":\"Java\",\n" + " \"price\":666.00\n" + "}", Book.class); System.out.println(book); } }
复制
此时,运行会抛出InvalidDefinitionException:
2、@JsonCreator
@JsonCreator可用在:
- 构造方法上
- 静态的工厂方法上
加了@JsonCreator注解,该类的对象在反序列化时,就走有@JsonCreator注解的方法来反序列化,原先无参+set的过程失效。
上面的Book类,可改成:
//@AllArgsConstructor @Getter public class Book { private String name; private Double price; @JsonCreator public Book(@JsonProperty("name") String name,@JsonProperty("price") Double price ){ System.out.println("@JsonCreator生效"); this.name = name; this.price = price+1; } }
复制
上面的@JsonProperty注解就是指定传参名和对象属性关系的,有点像MyBatis的@Param
public Book(@JsonProperty("name") String name,@JsonProperty("price") Double price ){
复制
@JsonCreator放在静态方法上就是:
@AllArgsConstructor @Getter public class Book { private String name; private Double price; @JsonCreator public static Book unSerialize(){ System.out.println("正在反序列化成对象"); return new Book("111",1.00); } }
复制
以@JsonCreator放在构造方法上为例,再运行前面的单元测试,看下反序列化的效果:
3、@JsonValue
@JsonValue注解可用在:
- get方法
- 属性字段上
一个类只能用一个,加上这个注解时,该类的对象序列化时就会只返回这个字段的值做为序列化的结果。
比如一个枚举类的get方法上加上该注解,那么在序列化这个枚举类的对象时,返回的就是枚举对象的这个属性,而不是这个枚举对象的序列化json串。继续改造上面的Book类:
@AllArgsConstructor @Getter public class Book { //@JsonValue,加get方法或者这个属性上,都一样 private String name; private Double price; @JsonCreator public static Book doSerialize(){ System.out.println("正在反序列化成对象"); return new Book("111",1.00); } /** * 序列化时,序列化成我return的值,即当前对象的name属性 */ @JsonValue public String getName(){ return this.name; } }
复制
单元测试中看下效果:
public class TestSome1 { @Test void testJson() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); Book book = objectMapper.readValue("{\n" + " \"name\":\"Java\",\n" + " \"price\":666.00\n" + "}", Book.class); System.out.println(book); System.out.println("======="); //序列化 System.out.println(objectMapper.writeValueAsString(book)); } }
复制
4、应用:枚举类中校验传参以及优化前后端数据交互
比如定义一个排序字段的枚举类,排序字段要拼到mapper层的SQL,所以其传参的合法性必须校验,每次在Service层校验,很繁琐。引入@JsonCreator:
@AllArgsConstructor @Getter public enum OrderFieldEnum { CREATE_TIME("createTime","create_time"), USER_NAME("userName","user_name"); private final String value; private final String field; private static final Map<String,OrderFieldEnum> map = new HashMap<>(3); @JsonCreator public static OrderFieldEnum unSerializer(String value){ //把以value为key,以枚举对象为value,存进map if(map.isEmpty()){ for (OrderFieldEnum fieldEnum : OrderFieldEnum.values()) { map.put(fieldEnum.value,fieldEnum); } } //map中找不到就是超出范围 if(!map.containsKey(value)){ throw new RuntimeException("超出范围"); } return map.get(value); } @JsonValue public String getValue(){ return this.value+"@JsonValue"; //这里刻意加个@JsonValue,方便后面验证序列化效果。return this.value就行。 } }
复制
此时,前端传个dto(json)过来,dto里有个参数的类型是枚举类型,反序列化json成dto对象时,枚举类型的属性也会反序列化,上面@JsonCreator定义的unSerializer方法执行,就会完成参数合法性校验,Service层省事了。至于序列化:
@JsonValue public String getValue(){ return this.value; }
复制
比如需要返给前端枚举值展示下拉框,就直接Vo里就直接List<Enum>
enumList,而不用定义个List<String>
enumList,再反复getValue:
enumList.add(ENUM1.getValue) enumList.add(ENUM2.getValue) ...
复制
因为返给前端时,Vo对象序列化,里面的一个个枚举对象也序列化,而JsonValue已经指定了序列化枚举对象时就把它的value返回就行。写个简单接口看下效果,定义个dto:
@Data public class JsonDto { private String id; private OrderFieldEnum orderField; }
复制
Service层demo:
@Service public class MyService { public List<OrderFieldEnum> listValue(JsonDto dto){ System.out.println(dto.getOrderField()); //直`List<Enum> List<OrderFieldEnum> list = List.of(OrderFieldEnum.CREATE_TIME, OrderFieldEnum.USER_NAME); return list; } }
复制
controller:
@RestController public class CodeController { @Resource private MyService myService; @PostMapping("/test") public List<OrderFieldEnum> getStr(@RequestBody JsonDto dto){ return myService.listValue(dto); } }
复制
异常传参时:
正常传参:
此时,不管枚举类型啥的,前端传个String就行。最后,注解的依赖坐标引对: