SpringBoot 接口对枚举类型的入参以及出参的转换处理

目录

  • 1、在项目中使用枚举类型
  • 2、不做任何处理的演示效果
    • 2.1、接口出参
    • 2.2、接口入参
  • 3、用枚举的code作为参数和返回值
    • 3.1 代码案例
      • 3.1.1、定义枚举基础接口`BaseEnum`,每个枚举都实现该接口
      • 3.1.2、性别Sex枚举并实现接口`BaseEnum`
      • 3.1.3、定义BaseEnum枚举接口序列化
      • 3.1.4、自定义Enum枚举接口反序列化
      • 3.1.5、配置自定义的BaseEnum类的序列化和Enum类的反序列
      • 3.1.6、配置类型转换工厂IntegerCodeToEnumConverterFactory和StringCodeToEnumConverterFactory
      • 3.1.7、将转换工厂注册到Spring MVC中
    • 3.2 演示效果
  • 4、项目代码地址

1、在项目中使用枚举类型

一般我们在项目中,设置表的类型字段,都是用int存储,用0、1、2…代表不同类型的含义。
以性别字段为例:
数据库表字段设计为integer:

CREATE TABLE t_user_info
(...sex INT NOT NULL COMMENT '性别,0-女,1-男'...
);

实体成员变量设计为枚举:

public class UserInfoEntity extends BaseField {.../** 性别,0-女,1-男 */private Sex sex;...
}@Getter
public enum Sex implements BaseEnum {woman(0, "女"), man(1, "男");@EnumValue //标记数据库存的值是sexprivate Integer code;private String desc;Sex(int code, String desc) {this.code = code;this.desc = desc;}public String getStrCode() {return code.toString();}
}

2、不做任何处理的演示效果

如果我们不做任何额外处理,当使用Sex枚举作为出入参时。只能返回manwoman或用manwoman作为参数。

2.1、接口出参

封装出参实体中sex成员变量类型为Sex枚举,则出参为:manwoman
在这里插入图片描述

2.2、接口入参

若是使用Sex枚举类型接收请求参数,不论是从接口get请求url中获取,还是使用构造实体接收post请求参数。都只能用manwoman作为参数,如果使用0或1作为参数,则无法正确被转换为Sex枚举。但是在开发过程中前端下拉选选项一般都与0,1,2…相对应。
在这里插入图片描述

在这里插入图片描述

3、用枚举的code作为参数和返回值

以上面的Sex枚举为例子,我希望在返回sex字段时,用0和1作为返回值,并且希望能用0和1映射男和女作为接口入参。该如何实现呢?

3.1 代码案例

3.1.1、定义枚举基础接口BaseEnum,每个枚举都实现该接口

/*** 枚举基础接口* 注意:不要构造枚举name和code相互交叉使用的枚举,否则在进行转换时将获取意外的结果。例如:*public enum Sex2 implements BaseEnum {*     woman("man", "女"), man("woman", "男");**     private String code;*     private String desc;**     Sex2(String code, String desc) {*         this.code = code;*         this.desc = desc;*     }*     public String getStrCode() {*         return code;*     }* }** 若是这种则是可以的:woman("woman", "女"), man("man", "男");*/
public interface BaseEnum {/*** 根据枚举值或名称从枚举类型type中获取枚举对象*/public static <T extends BaseEnum> T getEnum(Class<T> type, Object codeOrName) {T[] enums = type.getEnumConstants();for (T em : enums) {if (em.getStrCode().equals(codeOrName.toString()) || em.name().equals(codeOrName.toString())) {return em;}}return null;}/*** 获取枚举值的字符串code** @return 编码*/String getStrCode();/*** 获取枚举名称** @return 名称*/String name();
}

3.1.2、性别Sex枚举并实现接口BaseEnum

import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;@Getter
public enum Sex implements BaseEnum {woman(0, "女"), man(1, "男");@EnumValue //标记数据库存的值是sexprivate Integer code;private String desc;Sex(int code, String desc) {this.code = code;this.desc = desc;}public String getStrCode() {return code.toString();}
}

3.1.3、定义BaseEnum枚举接口序列化

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.study.enums.BaseEnum;
import java.io.IOException;/*** BaseEnum 序列化*/
public class BaseEnumSerializer extends JsonSerializer<BaseEnum> {@Overridepublic void serialize(BaseEnum value, JsonGenerator gen, SerializerProvider serializers) throws IOException {gen.writeNumber(value.getStrCode());// 增加一个字段,格式为【枚举类名称+Text】,存储枚举的namegen.writeStringField(gen.getOutputContext().getCurrentName() + "Text", value.name());}
}

3.1.4、自定义Enum枚举接口反序列化

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.study.enums.BaseEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import java.io.IOException;/*** BaseEnum反序列化*/
@Slf4j
public class BaseEnumDeserializer extends JsonDeserializer<Enum> {@Overridepublic Enum deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {JsonNode node = p.getCodec().readTree(p);String currentName = p.currentName();Object currentValue = p.getCurrentValue();Class findPropertyType = BeanUtils.findPropertyType(currentName, currentValue.getClass());if (findPropertyType == null) {log.info("在" + currentValue.getClass() + "实体类中找不到" + currentName + "字段");return null;}String asText = node.asText();if (StringUtils.isBlank(asText)) {return null;}if (BaseEnum.class.isAssignableFrom(findPropertyType)) {BaseEnum valueOf = null;if (StringUtils.isNotBlank(asText)) {valueOf = BaseEnum.getEnum(findPropertyType, asText);}if (valueOf != null) {return (Enum) valueOf;}}return Enum.valueOf(findPropertyType, asText);}
}

3.1.5、配置自定义的BaseEnum类的序列化和Enum类的反序列

配置Jackson在序列化BaseEnum和反序列化Enum时,使用我们自己定义的BaseEnumSerializer、EnumDeserializer进行处理。

因为Spring boot使用的是Jackson序列化接口出参的,所以若我们自定定义的枚举若是都实现了BaseEnum接口,那么就会调用BaseEnumSerializer序列化枚举对象,达到预期的效果。

package com.study.config;import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.study.enums.BaseEnum;
import com.study.serializer.EnumDeserializer;
import com.study.serializer.BaseEnumSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.TimeZone;/*** 加载baseEnum 构建bean定义,初始化Spring容器。*/
@Configuration
public class JacksonConfig {// JacksonConfig EnumBeanConfig@Beanpublic Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() {BaseEnumSerializer baseEnumSerializer = new BaseEnumSerializer();EnumDeserializer enumDeserializer = new EnumDeserializer();return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault())// 配置Jackson在序列化BaseEnum和反序列化Enum时,使用我们自己定义的BaseEnumSerializer、EnumDeserializer进行处理.serializerByType(BaseEnum.class, baseEnumSerializer).deserializerByType(Enum.class, enumDeserializer);}
}

3.1.6、配置类型转换工厂IntegerCodeToEnumConverterFactory和StringCodeToEnumConverterFactory

作用是当我们使用枚举类型接收接口入参时,会根据请求参数是Integer或者String调用各自的转换器转为BaseEnum枚举类型

import com.study.converters.IntegerToEnumConverter;
import com.study.enums.BaseEnum;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import java.util.HashMap;
import java.util.Map;/*** 枚举 转化器工厂类* 将Integer类型的参数转换为BaseEnum类型枚举对象* 参考:org.springframework.core.convert.support.IntegerToEnumConverterFactory*/
public class IntegerCodeToEnumConverterFactory implements ConverterFactory<Integer, BaseEnum> {private static final Map<Class, Converter> CONVERTERS = new HashMap<>();/*** 获取一个从 Integer 转化为 T 的转换器,T 是一个泛型,有多个实现** @param targetType 转换后的类型* @return 返回一个转化器*/@Overridepublic <T extends BaseEnum> Converter<Integer, T> getConverter(Class<T> targetType) {Converter<Integer, T> converter = CONVERTERS.get(targetType);if (converter == null) {converter = new IntegerToEnumConverter<>(targetType);CONVERTERS.put(targetType, converter);}return converter;}
}
import cn.hutool.core.util.ObjectUtil;
import com.study.enums.BaseEnum;
import org.springframework.core.convert.converter.Converter;
import java.util.HashMap;
import java.util.Map;/*** Integer类型参数枚举 转化器** @param <T>*/
public class IntegerToEnumConverter<T extends BaseEnum> implements Converter<Integer, T> {private Map<String, T> enumMap = new HashMap<>();public IntegerToEnumConverter(Class<T> enumType) {T[] enums = enumType.getEnumConstants();for (T e : enums) {enumMap.put(e.getStrCode(), e);}}@Overridepublic T convert(Integer source) {T t = enumMap.get(source);if (ObjectUtil.isNull(t)) {throw new IllegalArgumentException("无法匹配对应的枚举类型");}return t;}
}
import com.study.converters.StringToEnumConverter;
import com.study.enums.BaseEnum;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import java.util.HashMap;
import java.util.Map;/*** 枚举 转化器工厂类* 将String类型的参数转换为BaseEnum类型枚举对象* 参考:org.springframework.core.convert.support.StringToEnumConverterFactory*/
public class StringCodeToEnumConverterFactory implements ConverterFactory<String, BaseEnum> {private static final Map<Class, Converter> CONVERTERS = new HashMap<>();/*** 获取一个从 Integer 转化为 T 的转换器,T 是一个泛型,有多个实现** @param targetType 转换后的类型* @return 返回一个转化器*/@Overridepublic <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {Converter<String, T> converter = CONVERTERS.get(targetType);if (converter == null) {converter = new StringToEnumConverter<>(targetType);CONVERTERS.put(targetType, converter);}return converter;}
}
import cn.hutool.core.util.ObjectUtil;
import com.study.enums.BaseEnum;
import org.springframework.core.convert.converter.Converter;
import java.util.HashMap;
import java.util.Map;/*** String类型参数枚举 转化器** @param <T>*/
public class StringToEnumConverter<T extends BaseEnum> implements Converter<String, T> {private final Map<String, T> enumMap = new HashMap<>();public StringToEnumConverter(Class<T> enumType) {T[] enums = enumType.getEnumConstants();for (T e : enums) {enumMap.put(e.getStrCode(), e);enumMap.put(e.name(), e);}}@Overridepublic T convert(String source) {T t = enumMap.get(source);if (ObjectUtil.isNull(t)) {throw new IllegalArgumentException("无法匹配对应的枚举类型");}return t;}
}

3.1.7、将转换工厂注册到Spring MVC中

import com.study.converters.factory.IntegerCodeToEnumConverterFactory;
import com.study.converters.factory.StringCodeToEnumConverterFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebConfigurer implements WebMvcConfigurer {/*** 枚举类的转换器工厂 addConverterFactory*/@Overridepublic void addFormatters(FormatterRegistry registry) {registry.addConverterFactory(new IntegerCodeToEnumConverterFactory());registry.addConverterFactory(new StringCodeToEnumConverterFactory());}
}

3.2 演示效果

使用0、1、man、woman都可以请求接口
在这里插入图片描述
接口返回值为0、1。还新增了字段sexText保存枚举的名称
在这里插入图片描述

4、项目代码地址

项目gitee链接: https://gitee.com/WillDistance/mybatis-plus-h2.git

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/315092.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙原生应用/元服务开发-发布基础类型通知开发步骤

一、导入模块。 import NotificationManager from ohos.notificationManager;二、构造NotificationRequest对象&#xff0c;并发布通知。 1.普通文本类型通知由标题、文本内容和附加信息三个字段组成&#xff0c;其中标题和文本内容是必填字段。 let notificationRequest {i…

通过C++程序实现光驱的自动化刻录和读取

文章目录 ISO文件格式光盘的基本概念光盘种类特点DVDR光盘使用windows调用Linux调用Linux平台下用到的C库:读取设备驱动列表向光驱中写文件 数字存储媒体快速发展的今天&#xff0c;光驱的使用已经不像以前那样普及了。但是在数据备份、安装软件和操作系统、旧设备兼容等领域还…

ClickHouse基础知识(七):ClickHouse的分片集群

副本虽然能够提高数据的可用性&#xff0c;降低丢失风险&#xff0c;但是每台服务器实际上必须容纳全量数据&#xff0c;对数据的横向扩容没有解决。 要解决数据水平切分的问题&#xff0c;需要引入分片的概念。通过分片把一份完整的数据进行切 分&#xff0c;不同的分片分布到…

【Unity引擎技术整合】 Unity学习路线 | 知识汇总 | 持续更新 | 保持乐趣 | 共同成长

前言 本文对Unity引擎的知识进行了一个整理总结&#xff0c;基本包含了Unity中大部分的知识介绍。网上也有很多Unity相关的学习资料&#xff0c;但大多数都不成体系&#xff0c;学起来的时候难免会东奔西走的摸不着头脑。本文整理的多数文章都是有对应的系列性文章专栏&#x…

Windows本地如何部署Apache服务器搭配内网穿透实现无公网IP远程访问?

文章目录 前言1.Apache服务安装配置1.1 进入官网下载安装包1.2 Apache服务配置 2.安装cpolar内网穿透2.1 注册cpolar账号2.2 下载cpolar客户端 3. 获取远程桌面公网地址3.1 登录cpolar web ui管理界面3.2 创建公网地址 4. 固定公网地址 前言 Apache作为全球使用较高的Web服务器…

C# OpenCvSharp DNN Gaze Estimation

目录 介绍 效果 模型信息 项目 代码 frmMain.cs GazeEstimation.cs 下载 C# OpenCvSharp DNN Gaze Estimation 介绍 训练源码地址&#xff1a;https://github.com/deepinsight/insightface/tree/master/reconstruction/gaze 效果 模型信息 Inputs ----------------…

深度生成模型之自编码器与变分自编码器 ->(个人学习记录笔记)

文章目录 深度生成模型之自编码器与变分自编码器自编码器AE1. 定义2. 自编码器的应用 变分自编码器(VAE)1. 理论求解2. 模型求解3. 优化目标4. 再参数化策略 AE与VAE的对比AE与VAE的主要局限性 深度生成模型之自编码器与变分自编码器 自编码器AE 1. 定义 Auto-Encoder&#…

【EI会议征稿通知】第六届生物技术与生物医学国际学术会议(ICBB 2024)

第六届生物技术与生物医学国际学术会议 2024 6th International Conference on Biotechnology and Biomedicine (ICBB 2024) 第六届生物技术与生物医学国际学术会议&#xff08;ICBB 2024&#xff09;将于2024年03月29日-3月31日在中国-武汉市召开。ICBB 2024将围绕“生物技术…

C#/.NET/.NET Core推荐学习书籍(23年12月更新)

前言 古人云&#xff1a;“书中自有黄金屋&#xff0c;书中自有颜如玉”&#xff0c;说明了书籍的重要性。作为程序员&#xff0c;我们需要不断学习以提升自己的核心竞争力。以下是一些优秀的C#/.NET/.NET Core相关学习书籍&#xff0c;值得.NET开发者们学习和专研。书籍已分类…

深度理解Flutter:有状态Widget与无状态Widget的详细对比

有状态Widget 什么是有状态Widget (StatefulWidget) 官方解释&#xff1a; 如果用户与 widget 交互&#xff0c;widget 会发生变化&#xff0c;那么它就是 有状态的。 有状态的 widget 自身是可动态改变的&#xff08;基于State&#xff09;。 例如用户交互而改变 Widget 的 s…

发表《Nature》!美国研究团队发布可编程逻辑量子处理器

​&#xff08;图片来源&#xff1a;网络&#xff09; 近期&#xff0c;美国研究团队开发了一款可编程的逻辑量子处理器&#xff0c;并展示了可靠且可扩展的量子计算所需的关键要素&#xff0c;该成果已发表于《Nature》期刊&#xff08;doi&#xff1a;10.1038/s41586-023-06…

【网络面试(5)】收发数据及断开服务器(四次挥手)

前面了解到服务器和客户端在创建套接字&#xff0c;建立连接后&#xff0c;就可以进入到下一步&#xff0c;双发可以互相发送和接收数据&#xff0c;本篇博客就来学习一下这个过程。  我们印象里&#xff0c;发送数据应该是我们在浏览器输入网址&#xff0c;敲击回车的一瞬间&…