Spring后端直接用枚举类接收参数,自定义通用枚举类反序列化器

news2024/11/16 21:40:20

在使用枚举类做参数时,一般会让前端传数字,后端将数字转为枚举类,当枚举类很多时,很可能不知道这个code该对应哪个枚举类。能不能后端直接使用枚举类接收参数呢,可以,但是受限。
Spring反序列默认使用的是Jacskon,反序列化枚举类时,可以根据枚举类的name或ordinal属性进行反序列化,这是因为Jacskon内置了EnumDeserializer,它可以根据name或ordinal属性进行反序列化。

public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
    {
        if (p.hasToken(JsonToken.VALUE_STRING)) {
            return _fromString(p, ctxt, p.getText());
        }

        // But let's consider int acceptable as well (if within ordinal range)
        if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {
            if (_isFromIntValue) {
            	// 根据name
                return _fromString(p, ctxt, p.getText());
            }
   			// 根据ordinal
            return _fromInteger(p, ctxt, p.getIntValue());
        }
        if (p.isExpectedStartObjectToken()) {
            return _fromString(p, ctxt,
                    ctxt.extractScalarFromObject(p, this, _valueClass));
        }
        return _deserializeOther(p, ctxt);
    }

比如我们有一个接口:

    @PostMapping("/product")
    @ResponseBody
    public void product(@RequestBody Product product) {
        System.out.println(product.getStatus());
        System.out.println("ok");
    }

public class Product {
    private Status status;
    private String name;
    // getter and setter
}

public enum Status {
    ON_LINE(1000, "在线"),
    OFF_LINE(2000, "下线")
    ;
    private int code;
    private String desc;
    Status(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }
}

PostMan请求
在这里插入图片描述

这两种写法都可以。
status使用数字方式,只能是0或1,即枚举类的ordinal值.但是这种方式和我们的使用习惯不同,我们一般会自定义code,而不是使用ordinal。如果向根据自定义code反序列化枚举类,该如何实现呢?

参照Spring的方式,大概思路应该是自定义一个反序列化器,然后再需要使用自定义反序列化器的对象上加上@JsonDeserialize以覆盖Spring默认使用的EnumDeserializer。

自定义枚举类反序列化器

public class StatusDeser extends JsonDeserializer<Status> {
    @Override
    public Status deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
        final int code = p.getIntValue();
        return Status.getByCode(code);
    }
}

给Status增加一个getByCode,且指定反序列化器。

@JsonDeserialize(using = StatusDeser.class)
public enum Status {
    ON_LINE(1000, "在线"),
    OFF_LINE(2000, "下线")
    ;
    private int code;
    private String desc;
    Status(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public static Status getByCode(int code) {
        final Status[] values = Status.values();
        for (int i = 0; i < values.length; i++) {
            if (values[i].code == code) {
                return values[i];
            }
        }
        throw new RuntimeException("不合法的code值");
    }
}

此时就可以这样传值了
在这里插入图片描述
注意,如果直接使用枚举类做接收参数,接口和body应该这样写

    @PostMapping("/status")
    @ResponseBody
    public void status(@RequestBody Status status) {
        System.out.println(status);
        System.out.println("ok");
    }

在这里插入图片描述

统一处理枚举类

上面自定义的反序列化器可以处理某一种枚举,如果枚举很多,每写一个枚举类都要写一个与之对应的反序列化器,有点麻烦,而且一旦忘记写或不知道要写就麻烦了。如何对枚举类做统一处理呢?可以让枚举类都继承一个接口,我们的反序列化器对接口类型处理。

定义统一接口

public interface BaseEnum {
    Integer getCode();
}

统一接口中有一个返回code值的方法,前端传这个code值,我们根据这个code值反序列化出对应的枚举对象。

定义统一反序列化器

因为我们的接口下可以有多种实现类枚举,那我们在反序列化的时候要反序列化成哪种枚举类呢?怎么在运行时知道我们的目标枚举类呢?这个时候要使用StdDeserializer和ContextualDeserializer。
StdDeserializer中有目标对象类型,ContextualDeserializer可以在反序列化时获取目标对象类型信息。两者结合就可以在运行时获取到目标对象类型信息,动态创建反序列化器。
在这里插入图片描述

public class BaseEnumDeserial extends StdDeserializer<BaseEnum> 
								implements ContextualDeserializer {

    protected BaseEnumDeserial(Class<?> vc) {
        super(vc);
    }

    // Jackson通过反射创建BaseEnumDeserial对象,这个对象不会用于正真的反序列化,因为它没有真实的类信息
    // 真正用于反序列化的对象会通过createContextual重新创建
    public BaseEnumDeserial() {
        this(BaseEnum.class);
    }

    @Override
    public BaseEnum deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {

        // 反序列目标对象继承自BaseEnum且是枚举类型
        if (BaseEnum.class.isAssignableFrom(_valueClass) && Enum.class.isAssignableFrom(_valueClass)) {
            final int code = p.getIntValue();
            BaseEnum[] enumConstants = (BaseEnum[]) _valueClass.getEnumConstants();
            for (int i = 0; i < enumConstants.length; i++) {
                if (code == enumConstants[i].getCode()) {
                    return enumConstants[i];
                }
            }
        }
        return null;
    }

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
        // 从上下文获取目标对象类型
        final Class<?> rawClass = ctxt.getContextualType().getRawClass();
        // new出来的反序列化器不用我们缓存,一种类型的反序列化器的createContextual方法只会执行一次,执行后的结果Jackson自己会缓存
        return new BaseEnumDeserial(rawClass);
    }
}

注意,有的地方文章使用property获取目标对象类型,只有在枚举类作为其他对象的属性被反序列化时,property才有值,如果时直接反序列化枚举对象,则property是null,所以还是直接从上下文中取类型比较好。

// property可能为null
final Class<?> rawClass = property.getType().getRawClass();

枚举类改造

// 反序列器加在接口上,就不用在每个枚举类上加了
@JsonDeserialize(using = BaseEnumDeserial.class)
public interface BaseEnum {
    Integer getCode();
}
// 实现BaseEnum 接口
public enum Status implements BaseEnum {
    ON_LINE(1000, "在线"),
    OFF_LINE(2000, "下线")
    ;
    private int code;
    private String desc;
    Status(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }
	// 删除了getByCode方法

    @Override
    public Integer getCode() {
        return code;
    }
}

定义接口

    @PostMapping("/product")
    @ResponseBody
    // 枚举类在其他对象中
    public void product(@RequestBody Product product) {
        System.out.println(product.getStatus());
        System.out.println("ok");
    }

    @PostMapping("/status")
    @ResponseBody
    // 直接反序列化枚举类
    public void status(@RequestBody Status status) {
        System.out.println(status);
        System.out.println("ok");
    }

在这里插入图片描述
此时,就可以前端传数字,后端直接用枚举类型接收,不用再做数字到枚举类型的转化了。改造之后,下面这种方式就不行了,只能使用code反序列化,而不能使用name反序列化了。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

The NCCoE’s Automation of the CMVP

Earlier today at the ICMC24, we heard from a panel about the US National Cybersecurity Center of Excellence’s (NCCoE) work on the Automated Cryptographic Module Validation Program (ACMVP), which intends to tackle the troublingly long queue times we’ve se…

Apifox 「定时任务」操作指南,解锁自动化测试的新利器

定时任务是按照预设时间自动执行的任务&#xff0c;它可以有效解决一些常见问题&#xff0c;比如频繁执行的回归测试和大规模的接口测试&#xff0c;这些任务需要在固定时间点或间隔周期内自动运行&#xff0c;以确保软件的持续集成和持续交付过程中的稳定性和可靠性。通过使用…

实操学习——个人资料的录入、修改、密码的修改

实操学习——个人资料的录入、修改、密码的修改 一、个人资料的录入和修改知识补充&#xff1a;装饰器二、密码的修改知识补充&#xff1a;docker的关闭与启动 一、个人资料的录入和修改 在users的app下创建一个用户详情表 from django.contrib.auth.models import User from…

C/C++逆向:switch语句逆向分析

在逆向分析中&#xff0c;switch语句会被编译器转化为不同的底层实现方式&#xff0c;这取决于编译器优化和具体的场景。常见的实现方式包括以下几种&#xff1a; ①顺序判断&#xff08;if-else链&#xff09;&#xff1a; 编译器将switch语句转化为一系列的if-else语句。这…

【第十四章:Sentosa_DSML社区版-机器学习时间序列】

目录 【第十四章&#xff1a;Sentosa_DSML社区版-机器学习时间序列】 14.1 ARIMAX 14.2 ARIMA 14.3 HoltWinters 14.4 一次指数平滑预测 14.5 二次指数平滑预测 【第十四章&#xff1a;Sentosa_DSML社区版-机器学习时间序列】 14.1 ARIMAX 1.算子介绍 考虑其他序列对一…

Flutter鸿蒙化(windows)

Flutter鸿蒙化&#xff08;windows&#xff09; 参考资料Window配置Flutter的鸿蒙化环境下载配置环境变量HarmonyOS的环境变量配置配置Flutter的环境变量Flutter doctor -v 检测的问题flutter_flutter仓库地址的警告问题Fliutter doctor –v 报错[!] Android Studio (version 2…

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-18

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-18 1. The Application of Large Language Models in Primary Healthcare Services and the Challenges W YAN, J HU, H ZENG, M LIU, W LIANG - Chinese General Practice, 2024 人工智能大语言模型在基层医疗…

软媒市场新探索:软文媒体自助发布,开启自助发稿新篇章

在繁华喧嚣的软媒市场中,每一个声音都在竭力呼喊,每一个品牌都在奋力展现。而软文,作为一种温柔而坚韧的营销力量,正逐渐崭露头角。特别是软文媒体自助发布平台的出现,更是为企业提供了一个全新的、高效的自助发稿渠道。 软媒市场自助发布平台,正如其名,是一个让企业能够自主发…

离职员工客户如何管理?解锁2024企业微信新功能

公司里员工来来去去很正常&#xff0c;但每次有人走&#xff0c;老板们都会头疼&#xff0c;因为客户信息得有人接着管。客户对公司来说太重要了&#xff0c;不能丢。2024年&#xff0c;企业微信出了个新招&#xff0c;就是员工离职后&#xff0c;客户信息可以轻松转给新来的员…

JVM的基本概念

目录 一、JVM的内存划分 二、JVM的类加载过程 三、JVM的垃圾回收机制&#xff08;GC&#xff09; 四、分代回收 一、JVM的内存划分 一个运行起来的Java进程&#xff0c;就是一个Java虚拟机&#xff0c;就需要从操作系统中申请一大块内存。申请的内存会划分为不同的区域&…

Maven笔记(一):基础使用【记录】

Maven笔记&#xff08;一&#xff09;-基础使用 Maven是专门用于管理和构建Java项目的工具&#xff0c;它的主要功能有&#xff1a; 提供了一套标准化的项目结构 Maven提供了一套标准化的项目结构&#xff0c;所有IDE(eclipse、myeclipse、IntelliJ IDEA 等 项目开发工具) 使…

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-17

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-17 1. Large Language Models in Biomedical and Health Informatics: A Review with Bibliometric Analysis H Yu, L Fan, L Li, J Zhou, Z Ma, L Xian, W Hua, S He… - Journal of Healthcare …, 2024 生物…

HarmonyOS应用开发(组件库)--组件模块化开发、工具包、设计模式(持续更新)

致力于&#xff0c;UI开发拿来即用&#xff0c;提高开发效率 正则表达式...手机号校验...邮箱校验 文件判断文件是否存在 网络下载下载图片从沙箱中图片转为Base64格式从资源文件中读取图片转Base64 组件输入框...矩形输入框...输入框堆叠效果&#xff08;用于登录使用&#xf…

【自动驾驶】决策规划算法(二)参考线模块Ⅰ| 平滑算法与二次规划

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

(学习记录)使用 STM32CubeMX——GPIO引脚输入配置

STM32F103C8T6的GPIO引脚输入配置 时钟配置 &#xff08;学习记录&#xff09;使用 STM32CubeMX——配置时钟&#xff08;入门&#xff09;https://blog.csdn.net/Wang2869902214/article/details/142423522 GPIO 引脚输出配置 &#xff08;学习记录&#xff09;使用 STM32…

Springcloud框架-能源管理系统-能源管理系统源码-能源在线监测平台-双碳平台

一、介绍 基于SpringCloud的能管管理系统-能源管理平台源码-能源在线监测平台-双碳平台源码-SpringCloud全家桶-能管管理系统源码 有需者咨询&#xff0c;非诚勿扰&#xff1b; 二、软件架构 二、功能介绍 三、数字大屏展示 四、数据采集原理 五、软件截图

macos pyenv 安装python tk 、tkinter图形库方法步骤和使用总结

在macos中&#xff0c; pyenv 是一款用来管理多版本python 的工具&#xff0c; 我们常用的tk图形库是一个独立的工具库&#xff0c;我们在python里面使用的tkinter模块仅是调用这个独立的tk图形库&#xff0c; 所以如果我们希望在python里面使用它&#xff0c; 就必须要先安装t…

委托的注册及注销+观察者模式

事件 委托变量如果公开出去&#xff0c;很不安全&#xff0c;外部可以随意调用 所以取消public,封闭它&#xff0c;我们可以自己书写两个方法&#xff0c;供外部注册与注销&#xff0c;委托调用在子方法里调用&#xff0c;这样封装委托变量可以使它更安全&#xff0c;这个就叫…

金融加密机的定义与功能

金融加密机是一种用于保护金融交易数据和信息安全的重要安全设备。它通过硬件和软件的多重保障&#xff0c;确保金融交易中的敏感数据不被泄露或篡改。以下是关于金融加密机的详细介绍&#xff1a; 一、定义与功能 金融加密机是一种硬件安全设备&#xff0c;通过实现各种密码算…

深度deepin初体验(一)系统详细安装过程 | 国产系统

这里写自定义目录标题 深度deepin初体验&#xff08;一&#xff09;系统详细安装过程1.介绍2.安装要求3.环境4.创建虚拟机/系统升级系统选择语言硬盘分区备份文件拷贝系统重启常规设置 深度deepin初体验&#xff08;一&#xff09;系统详细安装过程 1.介绍 深度deepin是在debi…