Skip to content

Polymorphic Deserialization Based on Property (extend DEDUCTION) #4714

@kalyan-dass

Description

@kalyan-dass

Is your feature request related to a problem? Please describe.

Currently Polymorphic Deserialization without type indicator can be done in different ways

ID.DEDUCTION

@JsonTypeInfo(use= JsonTypeInfo.Id.DEDUCTION)
@JsonSubTypes({
        @JsonSubTypes.Type(value=ClassA.class),
        @JsonSubTypes.Type(value=ClassB.class)
})
static class SuperClass {
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
static class ClassA extends SuperClass {
    private int testId;
    private String status;
}

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ClassB extends SuperClass {
    private int testId2;
    private String status2;
}

@Test
void testJackson() throws JsonProcessingException {
    SuperClass temp = new ClassA(1, "status");
    SuperClass tempb = new ClassB(1, "status");
    var objectMapper = new ObjectMapper();
    var tempString = objectMapper.writeValueAsString(temp);
    var tempBString = objectMapper.writeValueAsString(tempb);
    var aResult = objectMapper.readValue(tempString, ClassA.class);
    var bResult = objectMapper.readValue(tempBString, ClassB.class);

    assertEquals(aResult.getClass(), ClassA.class);
    assertEquals(1, aResult.getTestId());
    assertEquals("status", aResult.getStatus());
    assertEquals(bResult.getClass(), ClassB.class);
    assertEquals(1, bResult.getTestId2());
    assertEquals("status", bResult.getStatus2());
}

`

Property Value

@JsonTypeInfo(use= JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "status")
@JsonSubTypes({
        @JsonSubTypes.Type(value=ClassA.class, name = "tempA"),
        @JsonSubTypes.Type(value=ClassB.class, name = "tempB")
})
static class SuperClass {
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
static class ClassA extends SuperClass {
    private int testId;
    private String status;
}

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ClassB extends SuperClass {
    private int testId;
    private String status;
}

@Test
void testJackson() throws JsonProcessingException {
    SuperClass temp = new ClassA(1, "tempA");
    SuperClass tempb = new ClassB(1, "tempB");
    var objectMapper = new ObjectMapper();
    var tempString = objectMapper.writeValueAsString(temp);
    var tempBString = objectMapper.writeValueAsString(tempb);
    var aResult = objectMapper.readValue(tempString, ClassA.class);
    var bResult = objectMapper.readValue(tempBString, ClassB.class);

    assertEquals(aResult.getClass(), ClassA.class);
    assertEquals(1, aResult.getTestId());
    assertEquals("tempA", aResult.getStatus());
    assertEquals(bResult.getClass(), ClassB.class);
    assertEquals(1, bResult.getTestId());
    assertEquals("tempB", bResult.getStatus());
}

`

As mentioned these are existing flow..

I'm proposing to Deserialize based on difference in property example as below ClassA having 2 and classB having 3.. When in a scenario

  1. Scenario - Input: {"testId":1,"status":"tempA"} - Map to ClassA
  2. Scenario - Input: {"testId":1,"status":"tempA","test":"abc"} - Map to ClassB
  3. Scenario - Input: {"testId":1,"test":"abc"} - Map to ClassB
  4. Scenario - Input: {"testId":1,"status":"tempA"} - Map to ClassA (Default)
  5. Scenario - Input: {"testId":1} - Map to ClassA (Default)
  6. Scenario - Input: {"status":"tempA"} - Map to ClassA (Default)
  7. Scenario - Input: {"test":"abc"} - Map to ClassB

`

/*
* 1. Differentiate based on Fields count that differ like ClassA has testId, status whereas ClassB has testId, status & test properties
* 2. May be specifying Ranking order which satisfies most
* 3. May be specifying which mandatory field would be differentiating each 
* */
@JsonTypeInfo(use= JsonTypeInfo.Id.NAME)
@JsonSubTypes({
        @JsonSubTypes.Type(value=ClassA.class),
        @JsonSubTypes.Type(value=ClassB.class)
})
static class SuperClass {
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
static class ClassA extends SuperClass {
    private int testId;
    private String status;
}

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ClassB extends SuperClass {
    private int testId;
    private String status;
    private String test;
}

@Test
void testJackson() throws JsonProcessingException {
    SuperClass temp = new ClassA(1, "tempA");
    SuperClass tempb = new ClassB(1, "tempB", "abc");
    var objectMapper = new ObjectMapper();
    var tempString = objectMapper.writeValueAsString(temp);
    var tempBString = objectMapper.writeValueAsString(tempb);
    var aResult = objectMapper.readValue(tempString, ClassA.class);
    var bResult = objectMapper.readValue(tempBString, ClassB.class);

    assertEquals(aResult.getClass(), ClassA.class);
    assertEquals(1, aResult.getTestId());
    assertEquals("tempA", aResult.getStatus());
    assertEquals(bResult.getClass(), ClassB.class);
    assertEquals(1, bResult.getTestId());
    assertEquals("tempB", bResult.getStatus());
    assertEquals("abc", bResult.getTest());
}

}
`

Describe the solution you'd like

  1. Differentiate based on Fields count that differ like ClassA has testId, status whereas ClassB has testId, status & test properties
  2. May be specifying Ranking order which satisfies most
  3. May be specifying which mandatory field would be differentiating each

Below scenario with which class to Map.. Please refer problem for ClassA & ClassB definition

  1. Scenario - Input: {"testId":1,"status":"tempA"} - Map to ClassA
  2. Scenario - Input: {"testId":1,"status":"tempA","test":"abc"} - Map to ClassB
  3. Scenario - Input: {"testId":1,"test":"abc"} - Map to ClassB
  4. Scenario - Input: {"testId":1,"status":"tempA"} - Map to ClassA (Default)
  5. Scenario - Input: {"testId":1} - Map to ClassA (Default)
  6. Scenario - Input: {"status":"tempA"} - Map to ClassA (Default)
  7. Scenario - Input: {"test":"abc"} - Map to ClassB

Usage example

Polymorphic definition based on type property and value is okay.. But based on class definition will be more useful

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    polymorphic-deductionIssues related to "Id.DEDUCTION" mode of `@JsonTypeInfo`

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions