Springmvc接⼝枚举类型数据格式化处理
⼀.背景简述
  ⾸先,我们都知道枚举实例有两个默认属性,name 和 ordinal,可通过 name()和ordinal()⽅法分别获得。其中 name 为枚举字⾯量(如MALE,FEMALE),ordinal 为枚举实例默认次序(从0开始)
  《阿⾥巴巴Java开发⼿册》将接⼝中枚举的使⽤分为两类,即接⼝参数和接⼝返回值,并规定:接⼝参数可以使⽤枚举类型,但接⼝返回值不可以使⽤枚举类型(包括含枚举类型的POJO对象)。
Java中出现的任何元素,在Gosling的⾓度都会有背后的思考和逻辑(尽管并⾮绝对完美,但Java的顶层抽象已经是天才级
了),⽐如:接⼝、抽象类、注解、和本⽂提到的枚举。枚举有好处,类型安全,清晰直接,还可以使⽤等号来判断,也可以⽤在switch中。它的劣势也是明显的,就是不要扩展。可是为什么在返回值和参数进⾏了区分呢,如果不兼容,那么两个都有问
题,怎么允许参数可以有枚举。当时的考虑,如果参数也不能⽤,那么枚举⼏乎⽆⽤武之地了。参数输出,毕竟是本地决定的,你本地有的,传送过去,向前兼容是不会有问题的。但如果是接⼝返回,就⽐较恶⼼了,因为解析回来的这个枚举值,可能本地还没有,这时就会抛出序列化异常。
⽐如:你的本地枚举类,有⼀个天⽓Enum:SUNNY, RAINY, CLOUDY,如果根据天⽓计算⼼情的⽅法:
guess(WeatcherEnum xx),传⼊这三个值都是可以的。返回值:Weather guess(参数),那么对⽅运算后,返回⼀个SNOWY,本地枚举⾥没有这个值,傻眼了
  当然,使⽤⾃定义字段 code 照样不能处理,对此,开发⼿册作者的回答如下
主要是从防⽌这种序列化异常⾓度来考虑,使⽤code⾄少不会出⼤乱⼦。⽽catch序列化异常,有点像
catch(NullPointerException e)⼀样代码过度,因为它是可预检异常。
  接⼝传值也不建议使⽤Ordinal,因为枚举⽰例应该是⽆序的,ordinal提供的顺序是不可靠的。所以我们应该使⽤⾃定义的枚举字段。
  我们定义了⼀个枚举类,继承了两个接⼝拥有两个字段,如下:
public interface ValueEnum<T> {
T value();
}
public interface DescriptionEnum {
String description();
}
public enum Gender implements ValueEnum<Integer>,DescriptionEnum {
/**
* 性别男
*/
MALE(1,"男"),
/**
* 性别⼥
*/
FEMALE(2,"⼥");
private Integer value;
private String description;
Gender(Integer value,String description) {
this.value = value;
this.description = description;
}
@Override
public String description() {
return description;
}
@Override
public Integer value() {
return value;
}
}
  有个使⽤Gender的pojo类User
@Data
public class User {
private Long id;
private String name;
private Gender gender;
private String email;
}
⼆. 使⽤枚举作为接⼝参数
  2.1 Spring 默认使⽤Bean接收枚举参数时⽀持字⾯量,这也是我们常见的做法。
    如下:
@Data
public class UserCommand {
private String name;
private Gender gender;
private String email;
}
@ApiOperation("添加⽤户")
@PostMapping("/users")
public User users(User command){
User user = new User();
return user;
}
  注意这种⽅式不⽀持枚举的ordinal值
  2.2 使⽤Json接收枚举参数
    Json数据都放在请求体中,后台使⽤注解@RequestBody+command bean接收(也可以从HttpServletRequest的getInputStream获取)
@ApiOperation("添加⽤户")
@PostMapping("/users")
public User users(@RequestBody UserCommand userCommand) {
User user = new User();
return user;
}
    这种⽅式⽀持字⾯量,ordinary 
  2.3 ⾃定义@RequestBody 和@ResponseBody处理枚举参数
   2.3.1 单独使⽤@JsonValue
public enum Gender implements ValueEnum<Integer>,DescriptionEnum{
/**
* 性别男
*/
MALE(10,"男"),
/**
* 性别⼥
*/
FEMALE(20,"⼥");
private Integer value;
private String description;
enum怎么用
Gender(Integer value,String description) {
this.value = value;
this.description = description;
}
@Override
public String description() {
return description;
}
@JsonValue
@Override
public Integer value() {
return value;
}
}
  @JsonValue决定了序列化的字段,表明该枚举类型只能使⽤该字段值传值。它可标注在字段和getter⽅法上,推荐标注在getter⽅法上。因为标注在字段上,swagger参数列表只显⽰字⾯值,但实际不能使⽤字⾯值传值,这样会给使⽤该接⼝的开发⼈员造成误解。
  标注value字段上:
  标注在value⽅法上
  这种⽅案虽然简单,但是只能单独使⽤某个字段传值。
  2.3.2 使⽤@JsonValue+@JsonCreator,代码如下public enum Gender implements ValueEnum<Integer>,DescriptionEnum{ /**
* 性别男
*/
MALE(10,"男"),
/**
* 性别⼥
*/
FEMALE(20,"⼥");
private Integer value;
private String description;
Gender(Integer value,String description) {
this.value = value;
this.description = description;
}
@JsonValue
@Override
public String description() {
return description;
}
@Override
public Integer value() {
return value;
}
@JsonCreator
public static Gender create(String value){
try{
return Gender.valueOf(value);
}catch (IllegalArgumentException e){
for (Gender gender : Gender.values()) {
try {
if (gender.value.equals(Integer.parseInt(value))) {
return gender;
}
}catch (NumberFormatException n) {
if (gender.description.equals(value)) {
return gender;
}
}
}
throw new IllegalArgumentException("No element matches "+value);            }
}
}
  @JsonValue是可选的,标注在getter⽅法上或者字段上,但是标注字段上Swagger显⽰参数不起作⽤,它可决定枚举反序列化的字段。如下
  @JsonCreator 标注在静态⽅法上,表明使⽤该⽅法序列化和反序列化,⽅法内部是序列化的逻辑
  上⾯的⽰例代码可使⽤三种⽅式传值。枚举类型的字⾯值,value属性或description属性,。这种⽅案就⽐较灵活可以任意决定⼀个或多个字段传值

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。