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
相关产品推荐
相关产品推荐

