-
-
Notifications
You must be signed in to change notification settings - Fork 137
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
[Avro] Add support for @Union
and polymorphic types
#60
[Avro] Add support for @Union
and polymorphic types
#60
Conversation
@@ -382,6 +390,17 @@ public final void writeStartObject() throws IOException { | |||
} | |||
|
|||
@Override | |||
public void writeStartObject(Object forValue) throws IOException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implemented because we need to know the actual value type to pick the correct branch of a union
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah good idea. While method was originally added for slightly different purpose (simply to allow keeping track of "current POJO"), it makes sense, and is great additional use.
The only (?) potential concern is to ensure that this method gets always called; mostly concerned wrt traversal through array/Collection serializers or such.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to maintain the old codepath as well, so if you call writeStartObject()
instead of writeStartObject(Object)
on a non-union, it shouldn't care.
Interesting. I think I'll have to contemplate bit more on this. It's potentially pretty cool, but I just want to convince myself that
I do hope to merge this in for 2.9, and I am very excited about all the great work you have done so far. This really makes 2.9 release quite interesting I think. |
The annotation always implies polymorphism, but a union in a schema does not imply polymorphism (as you have pointed out with nullable fields), and the Apache implementation does not enforce this during schema gen even though the deserializer assumes it to be true. This is one of many bugs in the Apache implementation that drove me to switch to Jackson for Avro encoding/decoding :) With the Apache implementation, the following is legal:
the resulting schema is The way I handled this in my implementation was:
This provided the expected behavior in cases where types match up with Type IDs correctly, and sane behavior when they don't (assume that I'm mapping to a different class that has similar or the same structure). |
@baharclerode Ok that works, and if need be can be changed as necessary. Scary thing about Avro impl just shoving value... did not know this occurs. :) |
Alright, cool. I'll get the final PR opened for the |
Unfortunately I think I will need to undo much of this PR. The way polymorphic handling is changed here is against the way Jackson handling is designed to be used, and although I understand it would fuilfill your needs I don't think it should be done this way. I don't know if there is a good way to solve the problem: fundamentally Jackson divides handling into two layers (which are mixed here):
In case of Avro the challenge is that schema would be needed as extra information for databinding. This patch works around this by always enabling potential polymorphic handling and rerouting handling with this assumption. So... without any other changes what I think of doing is to enabling polymorphic handling if This will also result in massive set of test failures, which I am trying to resolve now... |
One thing that I would accept, however... having a configurable setting (defaulting to disabled) which does allow aggressive polymorphic handling (that is, what this PR adds)? |
@baharclerode Actually... looks like maybe there is a simpler compromise. Looks like enabling equivalent of "default typing", but just for
What do you think? I'll go ahead and check this variant in; changes so far have been minor. I will go ahead and some more profiling; I think this approach should have very little overhead. |
I definitely see where you're coming from, and have been thinking about this since you first brought it up in the custom encoder PR. My thoughts were about just reducing the places where the
With these, we're only installing a type resolver in places that polymorphic type handling is mandatory, and letting the databinder do its normal behavior in the remaining places. The following update to the protected TypeResolverBuilder<?> _findTypeResolver(MapperConfig<?> config, Annotated ann, JavaType baseType) {
// If there's a custom deserializer, then it's responsible for handling any type information
if (config.getAnnotationIntrospector().findDeserializer(ann) != null) {
return StdTypeResolverBuilder.noTypeInfoBuilder();
}
Collection<NamedType> subtypes = null;
if (ann instanceof AnnotatedClass) {
subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(config, (AnnotatedClass) ann);
} else if (ann instanceof AnnotatedMember){
subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(config, (AnnotatedMember) ann, baseType);
}
if (!baseType.isAbstract() && (subtypes == null || subtypes.isEmpty())) {
return StdTypeResolverBuilder.noTypeInfoBuilder();
}
TypeResolverBuilder<?> resolver = new AvroTypeResolverBuilder();
JsonTypeInfo typeInfo = ann.getAnnotation(JsonTypeInfo.class);
if (typeInfo != null && typeInfo.defaultImpl() != JsonTypeInfo.class) {
resolver = resolver.defaultImpl(typeInfo.defaultImpl());
}
return resolver;
} |
My assumption here is that if support for Please let me know if some tests are failing after my latest commit(s): locally everything passes, but some tests seem to be skipped. And obviously I don't understand intent or (more) advanced use cases that well from Avro perspective. One thing on abstract types: I don't know if that is a valid assumption -- there are a few mechanisms that allow for mapping. For example, |
I just ran everything locally myself and they all passed. The 50 skipped tests represent features not supported by the apache implementation (but supported and tested in Jackson), or bugs in the apache implementation-- something to revisit when there's a new version of the apache implementation available, but otherwise skipping the tests is the easiest way to ignore them without making the test bed much more complex. Since your updates also fix the issue encountered in #71 I'll update that PR to just include the new tests (which also pass with your latest changes) so that those can be merged in. I think with your changes, this still covers my requirements at the moment, since all of my stuff will be annotated for the time being. Thank you! |
Alright, another stab at the native type ID stuff to support Generics / unions / polymorphic types. I think I managed to take care of the remaining comments that were left on the original PR, and additionally fixed handling of primitives when deserializing into
Object
(Note, this was quickly rebased at 2:20am... make sure I didn't accidentally revert your performance fixes. I think I preserved them...)