Search code examples
javaspring-bootjackson

Jackson Subtypes - polymorphic code - issue with parsing


Have the following sets of json from a third party API : First type:

{
  "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>”
    }
  }
}

Second Type:

{
  "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>”
    }
  }
}

The highlighted is the difference (these comments are not actually part of the JSON). I would like to leverage Jackson Subtypes to write cleaner code.

This is my code:

Top Level: 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;

   
}

@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 {

}

Derived Class:

@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;
    }
}

when Spring Boot ( 3.3.2) ,Jackson 2.17.2, de-serializes first type JSON MandateAuthDtls.AuthInfo is always null. My expectation is it should be PanInfo type.

Can I use the Subtypes feature of Jackson to handle the above scenario of the 2 JSONs? If not any better approach?


Solution

  • The problem is that "panInfo" is an object field so you need to define the relevant json object type. Here is an example.

    @JsonTypeInfo(
        ...
    )
    @JsonSubTypes({
        ...
        @JsonSubTypes.Type(value = PanInfoWrapper.class, name = "PAN")
    })
    public abstract class AuthInfo { }
    
    public class PanInfo { 
        private String pan;
        ...
    }
    
    public class PanInfoWrapper extends AuthInfo { 
        private PanInfo panInfo; 
    }
    

    For better readability, you can write inner class.