-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Serialization issue with polymorphism and not typed field #1594
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This is a regression in version 2.8.8; it works with 2.8.7. I was about to file a ticket for this. Since I also prepared a reproduction case, here it is: public final class Issue1594Test {
@JsonTypeInfo(include = As.PROPERTY, property = "type", use = Id.NAME)
@JsonSubTypes(@Type(name = "IMPL", value = SomeImpl.class))
public interface SomeIface {}
@JsonTypeName("IMPL")
public static final class SomeImpl implements SomeIface {}
@Test
public void testSerialization() throws IOException {
final ObjectMapper m = new ObjectMapper().registerModule(new Jdk8Module());
// Passes.
assertEquals(m.writeValueAsString(new SomeImpl()), "{\"type\":\"IMPL\"}");
// Fails. Yields "{}".
assertEquals(m.writeValueAsString(Optional.of(new SomeImpl())), "{\"type\":\"IMPL\"}");
}
} Since this affects uses of |
Hmm, @vsct-jburet, if your test fails with 2.8.7, then perhaps this is not exactly the same issue. Interesting. |
@vsct-jburet Code sample seems to be missing But I am not even quite sure how it should ever work: no type information exists to infer that contained value should be polymorphic. The only way for a property with So I am bit puzzled as to why this would ever have worked the way you want... Same applies to included unit test: I don't see how it should ever have worked to be honest. |
@cowtowncoder, I updated the unit test; the subtype now has a So to be clear: the issue also exists with the |
@Stephan202 No: what I mean is this: type of thing you serialize is seen as Note that this is different from passing It is very unfortunate if handling for Now: as to fixing usage: this is same as the problem with serializing/deserializing any generic types. List<MyPojo> stuff = new ArrayList<>();
// ...
String json = mapper.writeValueAsString(stuff);
// no type info, even if `MyPojo` is polymorphic, due to type erasure! and one solution is to explicitly specify type to use:
|
FWTW I think change was wrt fix for FasterXML/jackson-modules-java8#17 and specifically: FasterXML/jackson-modules-java8@92cb782 which does look for value serializer -- without wrapping for type serializer for type -- whereas old code was looking for possibly type-wrapped serializer. This would explain observed difference. @Stephan202 I think this is due to accidental detection/inclusion of type id handling for runtime type -- and as I said, this is not how detection should work, or how it's done for containers like I am not 100% sure how to proceed here. I would be willing to change behavior back for 2.8.x, wrt "accidentally working" (since consistency within a minor version 2.8.x is more important than fix to expected behavior across modules). But I also wouldn't want to break issue 17 that was fixed. |
@vsct-jburet on work-arounds: (1) is proper fix, not just work-around; (2) is incorrect in multiple ways (I can elaborate on that if anyone's interested). But there's 3rd possibility too:
which is alternative to (1). |
@cowtowncoder: thanks for the detailed analysis! Interestingly, I hit this issue in our code base in the context of a server-side Spring MVC test (using @RequestMapping(value = "/someresource/{id}", method = RequestMethod.GET)
public Optional<SomeResource> getResourceById(@PathVariable final String id) {
return this.myService.getResourceById(id);
} If I understand correctly, in this case the type information is not erased (right?). So should I file a ticket with the Spring project to ensure that code such as the above will also work in Jackson 2.9? (I assume that it's the responsibility of the Spring framework to provide this type information to Jackson.) |
@cowtowncoder here is the ValueTypeIdResolver (this a simplified version for the UT) : public class ValueTypeIdResolver extends TypeIdResolverBase {
public ValueTypeIdResolver() { }
public String idFromValue(Object value) { return "a"; }
public String idFromValueAndType(Object value, Class<?> suggestedType) { return "a"; }
public Id getMechanism() { return JsonTypeInfo.Id.NAME; }
@Override
public JavaType typeFromId(DatabindContext context, String id) throws IOException {
return context.getConfig().constructType(ValueA.class);
}
} But I don't understand why this should not work. Jackson knows the class at runtime, so why not using it? My use case : I want to serialize an object, but don't know the type. It could be a subclass of Value, or something else that does not implements Value. Deserialization is out of my scope, I focus only on serialization. So 1) and 3) do not match the use case. If you think it is out of the scope of Jackson, close the issue. I agree that it is a corner case. |
@Stephan202 If signature from method is passed in its generic resolvable form it should not be type erased, correct. Many/most frameworks don't do that, but perhaps Spring MVC does? |
@vsct-jburet Type of value is used for serialization; but for polymorphic base type it can not and should not used because it will not be available during deserialization -- during deserialization base type must be derived from declared type (or passed by caller in case of root value). I'll try again then. So: value serializer (contents) and type serializer (polymorphic type, iff enabled) are accessed separately. Former is based on actual runtime type (although optimized for However: it would seem to me that for this case, per-property annotation should work: class WithNoTypedValue {
@JsonTypeInfo(use= JsonTypeInfo.Id.NAME)
@JsonTypeIdResolver(ValueTypeIdResolver.class)
public Object v;
WithNoTypedValue(Object v) {
this.v = v;
}
} where you just need to enable polymorphic typing, along with custom type id resolver. Or: if you simply want any and all
and that would also force type inclusion. |
One more note: yes, I think we are talking about two different problems (but sort of related):
|
After much trial and error, I discovered that apparently Spring MVC handles the built-in A workaround for the issue with @JsonComponent
public class OptionalSerializer extends JsonSerializer<Optional<?>> {
@Override
public void serialize(Optional<?> value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value.isPresent()) {
gen.writeObject(value.get());
}
}
} @Stephan202 did you ever end up creating a ticket with Spring for this issue? |
@daniel-shuy I don't believe I did, sorry. Checking our internal git log, I see that back then I worked around it. That code has since been deleted. Our current code base doesn't seem to contain another instance of this issue. |
Not sure how to proceed with this, closing. May be re-filed with a test if reproducible with 2.10.3. |
Verified with 2.8.7 & 2.9.0.pr2
With theses classes:
This test fails:
Workarounds:
Type the value (ie :
public Value v;
)If you can't, force the serializer:
The text was updated successfully, but these errors were encountered: