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

Jackson Subtypes多态反序列化问题:AuthInfo解析始终为null

问题

从第三方API获取两种JSON结构,二者差异在于authMode对应的子节点(如authMode为PAN时对应panInfo,为custid时对应custidInfo)。希望借助Jackson Subtypes特性实现更简洁的多态反序列化,当前使用Spring Boot 3.3.2 + Jackson 2.17.2环境,反序列化第一种JSON时,MandateAuthDtls中的AuthInfo始终为null,期望能正确解析为PanInfo类型。请问是否可通过Jackson Subtypes解决该场景?若不可行,有何更优方案?

JSON示例

第一种类型

{
  "mandateAuthDtls": {
    "transactionID": "<Transaction ID>",
    "mndtType": "< AMEND / CANCEL / SUSPEND/ REVOKE /CUSTOM_CANCEL>",
    "mandateRequestDtl": {
      "MandateReqDoc": "<Encrypted and Signed request XML>"
    },
    /*SUBCLASS INFORMATION STARTS HERE*/
    "authMode": "PAN",
    "panInfo": {
      "pan": "<Encrypted PAN>"
    }
  }
}

第二种类型

{
  "mandateAuthDtls": {
    "transactionID": "<Transaction ID>",
    "mndtType": "<AMEND / CANCEL / SUSPEND/ REVOKE /CUSTOM_CANCEL>",
    "mandateRequestDtl": {
      "MandateReqDoc": "<Encrypted and Signed request XML>"
    },
    /*SUBCLASS INFORMATION STARTS HERE*/
    "authMode": "custid",
    "custidInfo": {
      "custid": "<Encrypted custid>"
    }
  }
}

当前代码

顶级类MandateAuthDtls

@Data
@Builder
@Jacksonized
@JsonFormat(with = JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
public class MandateAuthDtls {
    @JsonProperty("transactionID")
    private String transactionID;
    
    @JsonProperty("mandateType")
    private String mandateType;
    
    @JsonProperty("mandateRequestDtl")
    private MandateRequestDtl mandateRequestDtl;
    @JsonProperty("authMode")
    private String authMode;
    
   
    //@JsonProperty
    private AuthInfo authInfo;
}

抽象类AuthInfo

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
        property = "authMode",
        defaultImpl = CardInfo.class,
        visible = true
)
@JsonSubTypes({
    @JsonSubTypes.Type(value = AadharInfo.class, name = "Aadhaar"),
    @JsonSubTypes.Type(value = CustIDInfo.class, name = "custid"),
    @JsonSubTypes.Type(value = PanInfo.class, name = "PAN")
})
public abstract class AuthInfo {
}

子类PanInfo

@JsonTypeName("PAN")
public class PanInfo extends AuthInfo {
    @JsonProperty("Pan")
    private String pan;

    public String getPan() {
        return pan;
    }

    public void setPan(String pan) {
        this.pan = pan;
    }
}

解决方案

完全可以通过Jackson Subtypes解决该问题,当前authInfo为null是因为存在几处关键配置错误,修正步骤如下:

1. 修正MandateAuthDtls中authInfo的字段映射

JSON中对应AuthInfo的节点是panInfo/custidInfo,但当前authInfo字段未指定JsonProperty,且没有配置多别名支持。需要给authInfo添加@JsonProperty和@JsonAlias,覆盖所有可能的节点名:

@JsonProperty
@JsonAlias({"panInfo", "custidInfo", "aadhaarInfo"})
private AuthInfo authInfo;

2. 调整AuthInfo的类型信息配置

当前@JsonTypeInfo的include = JsonTypeInfo.As.EXTERNAL_PROPERTY配置是正确的(因为authMode和authInfo处于同一层级),但需注意:

  • 确保authMode的取值与@JsonSubTypes中的name完全匹配(比如PAN和custid大小写一致)
  • 若没有默认实现需求,可移除defaultImpl = CardInfo.class,避免引入不必要的依赖

3. 修正子类的字段名匹配

PanInfo中的@JsonProperty("Pan")与JSON里的pan大小写不一致,虽然开启了大小写不敏感,但建议保持一致避免潜在问题:

@JsonProperty("pan")
private String pan;

4. 验证Builder兼容性

使用@Jacksonized和@Builder时,需确保生成的Builder包含authInfo的设置方法,避免反序列化时无法赋值。可检查编译后的代码确认Builder是否正确生成该字段的操作方法。

最终修正后的核心代码示例

MandateAuthDtls调整后

@Data
@Builder
@Jacksonized
@JsonFormat(with = JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
public class MandateAuthDtls {
    @JsonProperty("transactionID")
    private String transactionID;
    
    @JsonProperty("mandateType")
    private String mandateType;
    
    @JsonProperty("mandateRequestDtl")
    private MandateRequestDtl mandateRequestDtl;
    
    @JsonProperty("authMode")
    private String authMode;
    
    @JsonProperty
    @JsonAlias({"panInfo", "custidInfo", "aadhaarInfo"})
    private AuthInfo authInfo;
}

PanInfo调整后

@JsonTypeName("PAN")
public class PanInfo extends AuthInfo {
    @JsonProperty("pan")
    private String pan;

    // getter和setter保持不变
}

替代方案(若Subtypes不适用)

如果第三方JSON结构过于灵活,Subtypes配置复杂,可选择以下方案:

  • @JsonAnySetter动态处理:在MandateAuthDtls中添加方法接收未匹配的属性,根据authMode手动实例化对应的AuthInfo子类
  • 自定义反序列化器:继承StdDeserializer<MandateAuthDtls>,在deserialize方法中手动解析authMode和对应子节点,构建AuthInfo实例

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 22:14:55