前后端数据传输: 利用 Jackson 注解实现 Enum 与 int 的双向映射

前后端交互:如何优雅处理 Java 枚举与 JSON 数字的自动转换?

在 Java 后端开发中,我们经常面临一个"数据格式不匹配"的通用问题:

  • 数据库/前端 :倾向于使用 int(如 1, 0)或 String 类型的状态码,因为它们传输快、存储小。
  • 后端代码 :倾向于使用 Enum(枚举),因为枚举具备强类型约束,能避免"魔法数字"(Magic Number),提高代码可读性。

如果直接返回枚举,默认会输出枚举的英文名称(如 "ENABLE");如果前端传数字 1,后端默认又无法自动转成枚举对象。

本文将介绍如何利用 Jackson(Spring Boot 默认的 JSON 框架)的两个核心注解 @JsonValue@JsonCreator,实现枚举对象JSON数值的无缝双向转换。

1. 场景复现:最简状态枚举

我们以最基础的"通用状态"为例。假设我们定义了一个包含 code 属性的枚举:

java 复制代码
package com.example.enums;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;

public enum CommonStatus {
    
    DISABLE(0, "禁用"),
    ENABLE(1, "启用");

    private final int code;
    private final String desc;

    CommonStatus(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    // ... 下面是核心代码 ...
}

2. 知识点详解

2.1 序列化难题:如何让 Enum 变成 int?

问题 :默认情况下,CommonStatus.ENABLE 序列化成 JSON 是 "ENABLE"。但前端往往需要数字 1

解决方案 :使用 @JsonValue

原理:该注解标记的方法,其返回值将作为整个对象序列化后的结果。

java 复制代码
    /**
     * 序列化 (Java -> JSON)
     * 标记此方法后,Jackson 会将枚举序列化为 code 的值 (0 或 1)
     * 而不是枚举的名字 ("DISABLE" 或 "ENABLE")
     */
    @JsonValue
    public int getCode() {
        return code;
    }

2.2 反序列化难题:如何让 int/String 变成 Enum?

问题 :前端传 JSON {"status": 1}{"status": "1"}。Jackson 默认是按照枚举的"名字"去匹配的,找不到名字叫 "1" 的枚举项,就会报错。

解决方案 :使用 @JsonCreator

原理 :该注解标记的静态方法(Static Method)将作为对象的"构造工厂"。Jackson 会把 JSON 中的值传给这个方法,由开发者自定义逻辑返回对应的枚举对象。

java 复制代码
    /**
     * 反序列化 (JSON -> Java)
     * 静态工厂方法:自定义解析逻辑
     * 
     * 技巧:参数使用 String 类型,可以同时兼容前端传 Number(1) 和 String("1")
     */
    @JsonCreator
    public static CommonStatus fromCode(String value) {
        if (value == null || value.trim().isEmpty()) {
            return null;
        }

        try {
            // 1. 将输入值转为 int
            int targetCode = Integer.parseInt(value);

            // 2. 遍历枚举找到匹配的 code
            for (CommonStatus status : CommonStatus.values()) {
                if (status.code == targetCode) {
                    return status;
                }
            }
        } catch (NumberFormatException e) {
            // 非数字字符串处理
        }

        // 3. 找不到匹配项,抛出异常提示参数错误
        throw new IllegalArgumentException("Invalid status code: " + value);
    }

3. 为什么这么做?(总结)

通过这两个注解的组合,我们达成了以下效果:

  1. 对外(前端/数据库) :接口表现为简单的数字(Integer),符合通用的接口规范,传输效率高。
  2. 对内(Java逻辑) :代码中完全使用枚举对象,享受强类型检查、switch-case 支持和方法封装的优势。
  3. 鲁棒性 :在 fromCode 方法中,我们兼容了字符串和数字的输入,极大地提高了接口的容错能力。
相关推荐
Rhys..1 小时前
Jenkinsfile保存在项目根目录下的好处
java·开发语言
间彧1 小时前
从 Docker Compose 到 Docker Swarm:RocketMQ 微服务项目集群部署实战指南
后端
iOS开发上架哦2 小时前
防止 iOS 应用被二次打包,从完整性校验到 IPA 成品混淆的多层安全方案
后端
讨厌下雨的天空2 小时前
线程同步与互斥
java·开发语言
IUGEI2 小时前
【计算机网络】HTTP/3如何实现可靠传输?
java·网络·后端·网络协议·tcp/ip·计算机网络·http
天下不喵2 小时前
安全小白入门(2)-----跨站脚本(XSS)
前端·后端·安全
谁黑皮谁肘击谁在连累直升机2 小时前
包及其导入
前端·后端
架构师专栏2 小时前
从 Spring Boot 3 升级到 4:完整迁移指南
spring boot·后端
u***u6853 小时前
JavaGraphQL案例
java·spring boot·后端