FasterXML/jackson-modules-base

Materialized generic class resolved incorrectly

shmosel opened this issue · 2 comments

When a generic interface is materialized with varying type parameters, it seems to confuse one for the other:

public static void main(String[] args) throws Exception {
    ObjectMapper mapper = new ObjectMapper()
            .registerModule(new MrBeanModule());

    mapper.readValue("{\"a\":{\"x\":\"1\"},\"b\":{\"x\":\"B\"}}", Outer.class);
}

public static class Outer {
    public Inner<Integer> a;
    public Inner<String> b;
}

public interface Inner<T> {
    T getX();
}

Output:

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.lang.Integer` from String "B": not a valid `java.lang.Integer` value
 at [Source: (String)"{"a":{"x":"1"},"b":{"x":"B"}}"; line: 1, column: 25] (through reference chain: Scratch$Outer["b"]->com.fasterxml.jackson.module.mrbean.generated.Scratch$Inner["x"])
	at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67)
	at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:1991)
	at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdStringValue(DeserializationContext.java:1219)
	at com.fasterxml.jackson.databind.deser.std.StdDeserializer._parseInteger(StdDeserializer.java:840)
	at com.fasterxml.jackson.databind.deser.std.StdDeserializer._parseInteger(StdDeserializer.java:820)
	at com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer.deserialize(NumberDeserializers.java:531)
	at com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer.deserialize(NumberDeserializers.java:506)
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:314)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
	at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:314)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
	at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4674)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3629)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3597)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
	at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:314)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177)
	at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4674)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3629)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3597)

Why does it think b.x should be an Integer?

I assume this is for Mr Bean module?

Correct: mr Bean cannot materialize generic types (ones with type parameters) in a way that generics are retained.
What probably happens is that the class being generated uses bindings of the first resolved type being passed: this will then have "correct" type bindings for that first type but not for others.

I wonder if module should rather throw an exception if encountering such case.
Or, alternatively, just not generate anything and let this case work the way it will (throwing exception unless something else materializes type).

Ideally it would of course create declarations that retain type parameter references.
But I don't think I'll have time to dig into this myself. Maybe someone else would tho.