Jackson deserialization: Prepare to deserialize subclasses outside original parent class JAR

32 views Asked by At

I have the following Java classes:


import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
@JsonSubTypes({
        @JsonSubTypes.Type(ChildAsJsonSubType.class),
})
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ParentClass {

  private String recipientUserId;
  private String initiatorUserId;

}

@Data
@NoArgsConstructor
public class ChildAsJsonSubType extends ParentClass {

  public ChildAsJsonSubType(String recipientUserId, String initiatorUserId) {
    super(recipientUserId, initiatorUserId);
  }

}

// not declareted in ParentClass's JsonSubTypes
@Data
@NoArgsConstructor
public class ChildAsJNOTsonSubType extends ParentClass {

  public ChildAsJNOTsonSubType(String recipientUserId, String initiatorUserId) {
    super(recipientUserId, initiatorUserId);
  }

}

Now I have the following Junit class to test deserialization:

class ParentClassTest {

  private final ObjectMapper mapper = new ObjectMapper();


  @DisplayName("Deserialize JSON representing a ParentClass subclass as instance of this subclass")
  @ParameterizedTest
  @ValueSource(classes = {
          ChildAsJsonSubType.class,
          ChildAsJNOTsonSubType.class
  })
  void testDeserializeSubclassesAsParentClassSubclass(Class<? extends ParentClass> ParentClassSubclass) throws JsonProcessingException {
    String recipientUserId = "0123456789abcdef";
    String initiatorUserId = "fedcba9876543210";
    var json = String.format("{\"@type\":\"%s\",\"recipientUserId\":\"%s\",\"initiatorUserId\":\"%s\"}",
            ParentClassSubclass.getSimpleName(), recipientUserId, initiatorUserId);

    ParentClass result = mapper.readValue(json, ParentClassSubclass);

    assertThat(result)
            .extracting(ParentClass::getRecipientUserId,
                    ParentClass::getInitiatorUserId)
            .containsExactly(recipientUserId, initiatorUserId);
  }

  @DisplayName("Deserialize JSON representing a ParentClass subclass as instance of ParentClass")
  @ParameterizedTest
  @ValueSource(classes = {
          ChildAsJsonSubType.class,
          ChildAsJNOTsonSubType.class
  })
  void testDeserializeSubclassesAsParentClass(Class<? extends ParentClass> ParentClassSubclass) throws JsonProcessingException {
    String recipientUserId = "0123456789abcdef";
    String initiatorUserId = "fedcba9876543210";
    var json = String.format("{\"@type\":\"%s\",\"recipientUserId\":\"%s\",\"initiatorUserId\":\"%s\"}",
            ParentClassSubclass.getSimpleName(), recipientUserId, initiatorUserId);

    ParentClass result = mapper.readValue(json, ParentClass.class);

    assertThat(result)
            .extracting(ParentClass::getRecipientUserId,
                    ParentClass::getInitiatorUserId)
            .containsExactly(recipientUserId, initiatorUserId);
  }

}

When I run the 1st test (testDeserializeSubclassesAsParentClassSubclass), It is Ok. When I run the 2nd one (testDeserializeSubclassesAsParentClass) for ChildAsJNOTsonSubType, I got this error:

com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'ChildAsJNOTsonSubType' as a subtype of ParentClass: known type ids = [ChildAsJsonSubType, ParentClass] at [Source: (String)"{"@type":"ChildAsJNOTsonSubType","recipientUserId":"0123456789abcdef","initiatorUserId":"fedcba9876543210"}"; line: 1, column: 10]

Of course, the solution would be simply add ChildAsJNOTsonSubType in ParentClass JsonSubTypes. I must say that originally ParentClass had @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS). However I had to change due to SonarQube problem java:S4544 (Using unsafe Jackson deserialization configuration is security-sensitive).

The problem, however is that there are ParentClass children classes outside its original module. i.e. in another JAR file. If I try run the tests above with these subclasses outside the original JAR of ParentClass, I probably I will get the same error as ChildAsJNOTsonSubType.

Therefore how I can emulate @JsonSubTypes / @JsonSubTypes.Type behavior for children classes that are not in the same JAR as parent class. May be some special annotation? Or a StdDeserializer implementation?

0

There are 0 answers