Deserialization of 2-dimensional arrays of final types fails when using DefaultTyping.NON_FINAL (original) (raw)

Hi,

I'm currently trying to serialize and deserialize a two-dimensional array of final types that are stored in a two-dimensional array of a non-final superclass (in the example below: storing a value of String[][] in a field of type Object[][]). In order to be able to reconstruct the type information, I activated default typing with DefaultTyping.NON_FINAL. However, during deserialization Jackson fails with com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type 'java.lang.String' from Array value (token 'JsonToken.START_ARRAY').

Following test case:

private static final class SomeBean {
    public Object[][] value;
}

public void testTwoDimensionalArrayMapping() throws JsonProcessingException {
    PolymorphicTypeValidator typeValidator = BasicPolymorphicTypeValidator.builder()
            .allowIfSubTypeIsArray()
            .allowIfSubType(Object.class)
            .build();

    ObjectMapper mapper = JsonMapper
            .builder()
            .activateDefaultTyping(typeValidator, NON_FINAL)
            .build();

    SomeBean instance = new SomeBean();

    // case 1 - successful
    instance.value = new Object[][]{new String[]{"1.1", "1.2"}, new String[]{"2.1", "2.2"}};
    String serialized = mapper.writeValueAsString(instance);
    SomeBean deserialized = mapper.readValue(serialized, SomeBean.class); // successful
    assertEquals(Object[][].class, deserialized.value.getClass());
    assertEquals(String[].class, deserialized.value[0].getClass());

    // case 2 - successful
    instance.value = new Object[][]{{"1.1", "1.2"}, {"2.1", "2.2"}};
    serialized = mapper.writeValueAsString(instance);
    deserialized = mapper.readValue(serialized, SomeBean.class); // successful
    assertEquals(Object[][].class, deserialized.value.getClass());
    assertEquals(Object[].class, deserialized.value[0].getClass());

    // case 3 (handcrafted JSON) - successful
    String handcrafted = "{\"value\":[\"[[Ljava.lang.String;\",[[\"1.1\",\"1.2\"],[\"2.1\",\"2.2\"]]]}";
    deserialized = mapper.readValue(handcrafted, SomeBean.class); // successful
    assertEquals(String[][].class, deserialized.value.getClass());
    assertEquals(String[].class, deserialized.value[0].getClass());

    // case 4 - fails
    instance.value = new String[][]{{"1.1", "1.2"}, {"2.1", "2.2"}};
    serialized = mapper.writeValueAsString(instance);
    deserialized = mapper.readValue(serialized, SomeBean.class); // fails
    assertEquals(String[][].class, deserialized.value.getClass());
    assertEquals(String[].class, deserialized.value[0].getClass());
}

The serialized strings are as follows:

  1. Successful: {"value":["[[Ljava.lang.Object;",[["[Ljava.lang.String;",["1.1","1.2"]],["[Ljava.lang.String;",["2.1","2.2"]]]]}
  2. Successful: {"value":["[[Ljava.lang.Object;",[["[Ljava.lang.Object;",["1.1","1.2"]],["[Ljava.lang.Object;",["2.1","2.2"]]]]}
  3. Successful (handcrafted JSON): {"value":["[[Ljava.lang.String;",[["1.1","1.2"],["2.1","2.2"]]]}
  4. Fails: {"value":["[[Ljava.lang.String;",[["[Ljava.lang.String;",["1.1","1.2"]],["[Ljava.lang.String;",["2.1","2.2"]]]]}

As far as I understand it, the problem is that the serializer and the deserializer are looking at different types in order to find out whether they are currently handling a non-final type:

Further information:

Full stack trace of the error:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.lang.String` from Array value (token `JsonToken.START_ARRAY`)
 at [Source: (String)"{"value":["[[Ljava.lang.String;",[["[Ljava.lang.String;",["1.1","1.2"]],["[Ljava.lang.String;",["2.1","2.2"]]]]}"; line: 1, column: 58] (through reference chain: com.fasterxml.jackson.databind.introspect.TwoDimensionalGenericArraysTest$SomeBean["value"]->java.lang.Object[][0]->java.lang.Object[][1])

    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1741)
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1515)
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1420)
    at com.fasterxml.jackson.databind.deser.std.StdDeserializer._parseString(StdDeserializer.java:1299)
    at com.fasterxml.jackson.databind.deser.std.StringArrayDeserializer.deserialize(StringArrayDeserializer.java:166)
    at com.fasterxml.jackson.databind.deser.std.StringArrayDeserializer.deserialize(StringArrayDeserializer.java:25)
    at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:214)
    at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:24)
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:120)
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromArray(AsArrayTypeDeserializer.java:53)
    at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserializeWithType(ObjectArrayDeserializer.java:246)
    at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserializeWithType(ObjectArrayDeserializer.java:24)
    at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:147)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:313)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:176)
    at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4620)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3575)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3543)
    at com.fasterxml.jackson.databind.introspect.TwoDimensionalGenericArraysTest.testTwoDimensionalArrayMapping(TwoDimensionalGenericArraysTest.java:56)