Search code examples
javajsonjacksondeserializationjackson-databind

JsonSubtype property value same as abstract class variable name


Sorry for the confusing title. I am using JsonSubTypes for a recent project of mine.

Please consider snippets below:

    @NoArgsConstructor
    @Data
    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "memberType")
    @JsonSubTypes({
            @JsonSubTypes.Type(name = UserType.AGENT_TEXT, value = Agent.class),
            @JsonSubTypes.Type(name = UserType.CUSTOMER_TEXT, value = Customer.class),
            @JsonSubTypes.Type(name = UserType.GUEST_TEXT, value = Guest.class)})
    public abstract class User {
    
        private UserType memberType;
    
        public User(UserType memberType) {
            this.memberType = memberType;
        }
    
        public abstract <T> T accept(UserVisitor<T> visitor);
    
        public abstract SomeObject buildAndGetSomeObject();
    }

and the derived concrete class with error mentioned in code comment:

@Data
@NoArgsConstructor
public class Customer extends User {

    private CustomerData userData;

    public Customer(CustomerData userData) {
        super( UserType.CUSTOMER);
        this.userData = userData;

    }

    @Override
    public <T> T accept(UserVisitor<T> visitor) {
        return visitor.visit(this);
    }

    @Override
    public SomeObject buildAndGetSomeObject() {
        return SomeObject.builder()
                .userType(getMemberType())   // getMemberType() here is null when ideally it should be CUSTOMER
                .build();
    }
}

Now, this code works fine for below payload:

{
  "User": {
    "memberType": "CUSTOMER",
    "memberType": "CUSTOMER",
    "userData": {
      "hashedId": "21039cefba8a499e85d62656cbandvcs234",
      "name": "******4321",
      "memberType": "CUSTOMER"
    }
  },
  
}

notice how memberType has been passed twice but same fails, for single occurrence as below:

{
  "User": {
    "memberType": "CUSTOMER",
    "userData": {
      "hashedId": "21039cefba8a499e85d62656cbandvcs234",
      "name": "******4321",
      "memberType": "CUSTOMER"
    }
  },
  
}

I am trying to understand what is possibly wrong in the approach / design here?

It works fine if i override getMemberType() inside Customer class and do a hardcoded return but then it would fail the purpose of inheritance.

My assumption is it has something to do with property = "memberType" and memberType variable name being same.

any help is appreciated.

This is in a dropwizard application with maven


Solution

  • solution was to use @JsonCreator on constructor. Seems JsonSubTypes by default called NoArgsConstructor to build object.

    On setting the JsonCreator, constructor got called correctly

    @JsonCreator        // voila
    @Builder
    public Customer(CustomerData userData) {
        super( UserType.CUSTOMER);
        this.userData = userData;
    
    }