Search code examples
javainheritancejacksonresponseobjectmapper

Inheritance DRY response


I have two class for external API response:

ClassA:

@JsonIgnoreProperties(ignoreUnknown = true)
@Getter
@Setter
public class ClassA {

  private SubClass subClass;

  @Getter
  @Setter
  public static class SubClass {

    Integer total;
    Integer perPage;
    List<Data> dataList;

    @Getter
    @Setter
    public static class Data {
       
      private String field1;
      private String field2;
      private int field3;
    }
  }
}

ClassB:

@JsonIgnoreProperties(ignoreUnknown = true)
@Getter
@Setter
public class ClassB {

  private SubClass subClass;

  @Getter
  @Setter
  public static class SubClass {

    Integer total;
    Integer perPage;
    List<Data> dataList;

    @Getter
    @Setter
    public static class Data {
       
      private String field1;
      private int field2;
      private Date field3;
    }
  }
}

And deserialization with ObjectMapper:

  ClassA classA =
      mapper.readValue(bodyResponse, ClassA.class);

What is the best way to get rid of duplicate code in these classes when using inheritance and later using deserialization?


Solution

  • You need to make root and sub classes generic. Use TypeReference to provide which data class you expect. Take a look at below example:

    import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.core.type.TypeReference;
    import com.fasterxml.jackson.databind.json.JsonMapper;
    import lombok.Getter;
    import lombok.Setter;
    import lombok.ToString;
    
    import java.util.Date;
    import java.util.List;
    
    public class GenericDataApp {
        public static void main(String[] args) throws JsonProcessingException {
            JsonMapper mapper = JsonMapper.builder().build();
            String srcA = "{\"subClass\":{\"total\":1,\"dataList\":[{\"field2\":\"String value\"}]}}";
            RootClass<DataA> dataA = mapper.readValue(srcA, new TypeReference<RootClass<DataA>>() {
            });
            System.out.println(dataA.getSubClass());
    
            String srcB = "{\"subClass\":{\"total\":1,\"dataList\":[{\"field2\":12}]}}";
            RootClass<DataB> dataB = mapper.readValue(srcB, new TypeReference<RootClass<DataB>>() {
            });
            System.out.println(dataB.getSubClass());
        }
    }
    
    @JsonIgnoreProperties(ignoreUnknown = true)
    @Getter
    @Setter
    class RootClass<T> {
    
        private SubClass<T> subClass;
    
        @Getter
        @Setter
        @ToString
        public static class SubClass<T> {
    
            Integer total;
            Integer perPage;
            List<T> dataList;
        }
    }
    
    @Getter
    @Setter
    @ToString
    class DataA {
    
        private String field1;
        private String field2;
        private int field3;
    }
    
    @Getter
    @Setter
    @ToString
    class DataB {
    
        private String field1;
        private int field2;
        private Date field3;
    }
    

    Above code prints:

    RootClass.SubClass(total=1, perPage=null, dataList=[DataA(field1=null, field2=String value, field3=0)])
    RootClass.SubClass(total=1, perPage=null, dataList=[DataB(field1=null, field2=12, field3=null)])