Non-generic interface method hides type resolution info from generic base class
See original GitHub issueI saw this issue via MongoJack and haven’t yet been able to construct a pure jackson databind test case but it seems that in some circumstances a class definition like the following:
public interface I {
String getT();
}
public class A<T> {
private T t;
public A(T t) { this.t = t; }
public T getT() { return t; }
}
public class B extends A<String> implements I {
public B(String t) { super(t); }
}
can result in the type information required to resolve the type of t
to a String is lost.
This happens because in AnnotatedClass._addMemberMethods is this code:
/* 06-Jan-2010, tatu: [JACKSON-450] Except that if method we saw first is
* from an interface, and we now find a non-interface definition, we should
* use this method, but with combination of annotations.
* This helps (or rather, is essential) with JAXB annotations and
* may also result in faster method calls (interface calls are slightly
* costlier than regular method calls)
*/
if (old.getDeclaringClass().isInterface() && !m.getDeclaringClass().isInterface()) {
methods.add(old.withMethod(m));
}
In my test, the interface getter is discovered first and so plays the role of “old”, then the base class getter is being added and so the original definintion of the method is simply adjusted with a reference to the method being processed. However the method from the interface has no type resolution information since the interface is not defined with generics so the type resolution information is lost and thus t
can end up being serialialised as Object instead of String and so a serialisation failure occurs.
I implemented a fix as follows. To AnnotatedMethod add the method:
public AnnotatedMethod withContextAndMethod(TypeResolutionContext ctxt, Method m) {
return new AnnotatedMethod(ctxt, m, _annotations, _paramAnnotations);
}
and then change the code in AnnotatedClass to:
if (old.getDeclaringClass().isInterface() && !m.getDeclaringClass().isInterface()) {
methods.add(old.withContextAndMethod(typeContext, m));
}
which resolved my problem. Happy to submit a pull request, but I haven’t got the CLA ready yet and I would also like some help constructing a pure Jackson unit test to replicate the issue.
To be specific about my MongoJack reproducer:
public interface I {
String getT();
}
public class A<T> {
private T t;
public A(T t) { this.t = t; }
public T getT() { return t; }
}
public class B extends A<String> implements I {
public B(String t) { super(t); }
}
@Test
public void test() {
ObjectMapper om = new ObjectMapper();
MongoJackModule.configure(om);
DBQuery.Query query = DBQuery.is("_id", "t");
JavaType bType = om.getTypeFactory().constructType(B.class);
DBObject serialized = SerializationUtils.serializeQuery(om, bType, query);
System.out.println(serialized);
}
Prints { "_id": "t" }
as expected with my fix and throws this exception without it:
org.mongojack.MongoJsonMappingException: Error serializing value t in DBQuery operation _id
at org.mongojack.internal.util.SerializationUtils.serializeQueryField(SerializationUtils.java:266)
at org.mongojack.internal.util.SerializationUtils.serializeQueryCondition(SerializationUtils.java:182)
at org.mongojack.internal.util.SerializationUtils.serializeQuery(SerializationUtils.java:155)
at org.mongojack.internal.util.SerializationUtils.serializeQuery(SerializationUtils.java:143)
at bugs.jackson.selfpolymorph.test(selfpolymorph.java:69)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class java.lang.String and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:284)
at com.fasterxml.jackson.databind.SerializerProvider.mappingException(SerializerProvider.java:1110)
at com.fasterxml.jackson.databind.SerializerProvider.reportMappingProblem(SerializerProvider.java:1135)
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:69)
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:32)
at org.mongojack.internal.util.SerializationUtils.serializeQueryField(SerializationUtils.java:264)
... 27 more
My testing has been using the 2.8 branch of jackson databind.
Issue Analytics
- State:
- Created 6 years ago
- Comments:11 (5 by maintainers)
Top GitHub Comments
Thanks for this @cowtowncoder, @tbartley
Came across this issue today. Not sure if it was intentional - the line that fixes the issue, as I am experiencing it, was commented out. Comment here: https://github.com/FasterXML/jackson-databind/commit/48fc70bfd3938440782b66ced08c3420ac2dbfa1#commitcomment-24618605
Actually, since this was in 2.9.1, I can re-open, re-fix it without too much confusion…