FasterXML / FasterXML/jackson-databind
Inner classes that get their generic information from an outer class lose type information
- Dominant language
- Java
- Stars
- 3.7k
- Forks
- 1.5k
- Avg merge
- 3d 6h
- Merged PRs (30d)
- 28
Description
**Describe the bug**
If a non-static inner class uses generics from the outer class, that generic information is lost when serializing.
**Version information**
2.12.5
**To Reproduce**
```java
import static org.assertj.core.api.Assertions.assertThat;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
public final class JacksonPoC {
@JsonTypeInfo(use = Id.NAME)
@JsonSubTypes({@JsonSubTypes.Type(value = ConcreteValue.class, name = "concrete-value")})
private interface AbstractValue {}
private static class ConcreteValue implements AbstractValue {}
public interface Wrapper {
T getValue();
}
private static final class Outer {
private final class Inner implements Wrapper {
private final T value;
Inner(T value) {
this.value = value;
}
@Override
public T getValue() {
return value;
}
}
Inner create(T value) {
return new Inner(value);
}
}
private static final class Direct implements Wrapper {
private final T value;
Direct(T value) {
this.value = value;
}
@Override
public T getValue() {
return value;
}
}
private static final class Container {
private final Wrapper wrapper;
Container(Wrapper wrapper) {
this.wrapper = wrapper;
}
public Wrapper getWrapper() {
return wrapper;
}
}
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void direct() throws Exception {
// This test passes because the generic information is present on the class directly
Container object = new Container(new Direct<>(new ConcreteValue()));
assertThat(mapper.writeValueAsString(object))
.isEqualTo("{\"wrapper\":{\"value\":{\"@type\":\"concrete-value\"}}}");
}
@Test
void indirect() throws Exception {
// This test fails (the @type part is omitted) because the `AbstractValue` information is lost
Container object = new Container(new Outer().create(new ConcreteValue()));
assertThat(mapper.writeValueAsString(object))
.isEqualTo("{\"wrapper\":{\"value\":{\"@type\":\"concrete-value\"}}}");
}
}
```
**Additional context**
The example above does feel quite contrived, but I encountered this in practice with Guava's Multimaps.asMap on a SetMultimap, because the resulting class `AbstractMapBasedMultimap.WrappedSet` inherits the generic information from the parent.
When debugging this, I got as far as the short-circuit in https://github.com/FasterXML/jackson-databind/blob/77e8b62fed4748d19fbd350c9fd80a7bdca75c91/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java#L487, but I'm not sure whether simply removing that is sufficient to resolve this.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.