Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Polymorphic Deserialization Based on Property #4714

Open
kalyan-dass opened this issue Sep 24, 2024 · 3 comments
Open

Polymorphic Deserialization Based on Property #4714

kalyan-dass opened this issue Sep 24, 2024 · 3 comments

Comments

@kalyan-dass
Copy link

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

@kalyan-dass kalyan-dass added the to-evaluate Issue that has been received but not yet evaluated label Sep 24, 2024
@JooHyukKim
Copy link
Member

Could you try JsonTypeInfo.Id.DEDUCTION?
Or you can implement custom TypeIdResolver which would be more flexible.

@kalyan-dass
Copy link
Author

When used JsonTypeInfo.Id.DEDUCTION it asking for unique properties between classes..

When TypeIdResolver is used I might need to create multiple Resolver for each MainType

@cowtowncoder
Copy link
Member

cowtowncoder commented Sep 25, 2024

While we can leave this issue open, I do not think we will be pursuing anything like this: existing options can be improved but I really do not want to further complicate logic of polymorphic type resolution.

Specifically, no plans to combine DEDUCTION approach with properties-based variants: I am still not sure if adding DEDUCTION was a good idea or not -- my main concern was and is that while it can handle many cases ok it just opens up so many other cases users might want to tackle, but that cannot be handled due to limitations of design/architecture.

@cowtowncoder cowtowncoder removed the to-evaluate Issue that has been received but not yet evaluated label Sep 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants