You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot JSON反序列化:合并两个字段为实体类字段问题

解决Spring Boot中平级JSON字段反序列化为嵌套实体类的问题

问题根源

请求JSON里的countryCode和mobileNumber是平级字符串字段,但RegistrationRequest中的mobilePhoneNumber是MobileNumber类型对象。Jackson默认会尝试把mobileNumber字符串直接反序列化为MobileNumber对象,导致自定义反序列化器只能拿到mobileNumber对应的文本节点,无法访问同级的countryCode字段。


方案1:使用@JsonCreator构造函数(推荐)

直接在RegistrationRequest中添加带@JsonCreator注解的构造函数,接收平级的countryCode和mobileNumber参数,手动组装成MobileNumber对象,无需自定义反序列化器:

public class RegistrationRequest {
    // 其他业务字段
    private final MobileNumber mobilePhoneNumber;

    // 构造函数需包含所有业务字段,以下为核心部分示例
    @JsonCreator
    public RegistrationRequest(
            // 其他字段参数,示例:@JsonProperty("email") String email,
            @JsonProperty("countryCode") String countryCode,
            @JsonProperty("mobileNumber") String mobileNumber) {
        // 初始化其他字段
        // this.email = email;
        this.mobilePhoneNumber = new MobileNumber(mobileNumber, countryCode);
    }

    // 提供mobilePhoneNumber的getter方法
    public MobileNumber getMobilePhoneNumber() {
        return mobilePhoneNumber;
    }

    // 其他字段的getter方法
}

同时可简化MobileNumber类的注解(无需单个字符串反序列化的配置):

@Getter
@Setter
@EqualsAndHashCode
@ToString
@AllArgsConstructor
public class MobileNumber {
    private String number;
    private String countryCode;
}

方案2:自定义RegistrationRequest级别的反序列化器

如果必须保留自定义反序列化逻辑,将反序列化器移到RegistrationRequest类上,这样就能获取整个JSON根节点,从而访问countryCode字段:

修改RegistrationRequest类

@JsonDeserialize(using = RegistrationRequestDeserializer.class)
public class RegistrationRequest {
    // 其他业务字段
    private final MobileNumber mobilePhoneNumber;

    // 私有构造函数,仅供反序列化器调用
    private RegistrationRequest(/* 其他字段参数, */ MobileNumber mobilePhoneNumber) {
        // 初始化其他字段
        this.mobilePhoneNumber = mobilePhoneNumber;
    }

    // 提供所有字段的getter方法
    public MobileNumber getMobilePhoneNumber() {
        return mobilePhoneNumber;
    }
}

编写RegistrationRequestDeserializer

public class RegistrationRequestDeserializer extends StdDeserializer<RegistrationRequest> {

    private static final long serialVersionUID = 1L;

    protected RegistrationRequestDeserializer() {
        super(RegistrationRequest.class);
    }

    @Override
    public RegistrationRequest deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
        JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser);

        // 提取其他业务字段,示例:String email = rootNode.get("email").asText();

        // 提取手机号相关字段
        String countryCode = rootNode.has("countryCode") ? rootNode.get("countryCode").asText() : "44";
        String mobileNumber = "";
        if (rootNode.has("mobilePhoneNumber")) {
            mobileNumber = rootNode.get("mobilePhoneNumber").asText();
        } else if (rootNode.has("phoneNumber")) {
            mobileNumber = rootNode.get("phoneNumber").asText();
        } else if (rootNode.has("mobileNumber")) {
            mobileNumber = rootNode.get("mobileNumber").asText();
        }

        MobileNumber mobilePhoneNumber = new MobileNumber(mobileNumber, countryCode);

        // 构造RegistrationRequest对象,传入其他字段和mobilePhoneNumber
        return new RegistrationRequest(/* email, */ mobilePhoneNumber);
    }
}

方案3:调整请求JSON结构(备选)

如果允许修改请求格式,将countryCode和mobileNumber嵌套到mobilePhoneNumber对象中,原自定义反序列化器即可正常工作:

{
    // 其他字段
    "mobilePhoneNumber": {
        "countryCode": "44",
        "mobileNumber": "07545878096"
    }
}

内容的提问来源于stack exchange,提问作者cyberscientist

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 03:15:42