前后端数据传输: 利用 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 方法中,我们兼容了字符串和数字的输入,极大地提高了接口的容错能力。
相关推荐
JavaGuide1 天前
7 道 RAG 基础概念知识点/面试题总结
前端·后端
桦说编程1 天前
从 ForkJoinPool 的 Compensate 看并发框架的线程补偿思想
java·后端·源码阅读
格砸1 天前
从入门到辞职|从ChatGPT到OpenClaw,跟上智能时代的进化
前端·人工智能·后端
蝎子莱莱爱打怪1 天前
GitLab CI/CD + Docker Registry + K8s 部署完整实战指南
后端·docker·kubernetes
躺平大鹅1 天前
Java面向对象入门(类与对象,新手秒懂)
java
哈密瓜的眉毛美1 天前
零基础学Java|第三篇:DOS 命令、转义字符、注释与代码规范
后端
用户60572374873081 天前
AI 编码助手的规范驱动开发 - OpenSpec 初探
前端·后端·程序员
哈密瓜的眉毛美1 天前
零基础学Java|第二篇:Java 核心机制与第一个程序:从 JVM 到 Hello World
后端
用户8307196840821 天前
RabbitMQ vs RocketMQ 事务大对决:一个在“裸奔”,一个在“开挂”?
后端·rabbitmq·rocketmq