Polymorphic resolution without annotations, looking for working example

3,656 views
Skip to first unread message

calzo...@gmail.com

unread,
Jul 29, 2014, 8:32:09 AM7/29/14
to jackso...@googlegroups.com
Hey there,

I am currently trying to handle polymorphic resolution without using the annotations and unfortunately, I haven't found any completely working example yet. Why I'm not using the existing annotations is that the application for which I'm building Restful Services, is mainly developed by external staff. So as the application grows, there will be more and more subtypes to deserialize implemented by the external staff and I cannot check/rework each subtype for annotations. Therefore my plan is to use a CustomTypeResolverBuilder to which I'm registering all subtypes in a generic way.
What I have found so far is getting me close to a solution but still not working (I extended this example with a SubtypeResolver and for testing reasons registered some Subtypes manually).
Does anybody have a suitable example for me?

Best regards,

Al

Tatu Saloranta

unread,
Jul 31, 2014, 3:53:54 PM7/31/14
to jackso...@googlegroups.com
I know this may not be an option, but... would it be possible to just use class names as type ids? Jackson allows this as well as type names, and physical class name does not need registration. It is more fragile way, and assumes use of Java on both sides. But can be much simpler way if feasible.

-+ Tatu +-


--
You received this message because you are subscribed to the Google Groups "jackson-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jackson-user...@googlegroups.com.
To post to this group, send email to jackso...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

calzo...@gmail.com

unread,
Aug 1, 2014, 4:14:56 AM8/1/14
to jackso...@googlegroups.com
First of all, thank you for your advice. I might not fully get the idea, but wouldn't your suggestion mean that I would have to do some minimal annotation in each subclass like "@type" : "Employee" (taken from http://wiki.fasterxml.com/JacksonPolymorphicDeserialization)? Or is there another option to achieve this kind of resolution without any annotation that I might have missed?

Tatu Saloranta

unread,
Aug 1, 2014, 3:12:26 PM8/1/14
to jackso...@googlegroups.com
No, kind of type identifier to use is specified by @JsonTypeInfo (either direct, or via mix-in, or even just "injected" by custom AnnotationIntrospector), like so:

    @JsonTypeInfo(use=Id.CLASS, include=As.PROPERTY, property="class")

(instead of 'use=Id.NAME')

-+ Tatu +-

Message has been deleted

calzo...@gmail.com

unread,
Aug 4, 2014, 11:35:00 AM8/4/14
to jackso...@googlegroups.com
Ah ok, got it. Unfortunately, this possibility is resulting in an "invalid type id (for id type Id.class)"-exception. The other option @JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property="type")
is resulting in Could not resolve type id 'xyz' into a subtype of [simple type, class org.app.logic.api.abc].

What I did so far was implementing some kind of MapperFactory with a creation method:

 public ObjectMapper create() {
   
ObjectMapper mapper = new ObjectMapper();
    mapper
.enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.NON_FINAL, "@class");
   
CustomTypeResolverBuilder resolverBuilder = new CustomTypeResolverBuilder();
    resolverBuilder
.init(JsonTypeInfo.Id.CLASS, null).inclusion(JsonTypeInfo.As.PROPERTY);
    resolverBuilder
.typeProperty("@Class");
    mapper
.setDefaultTyping(resolverBuilder);
   
SubtypeResolver subtypeResolver = mapper.getSubtypeResolver();
    subtypeResolver
.registerSubtypes(new NamedType(uvw.class, "Uvw"));
    subtypeResolver
.registerSubtypes(new NamedType(xyz.class, "Xyz"));
    mapper
.setSubtypeResolver(subtypeResolver);
   
return mapper;
 
}

Tatu Saloranta

unread,
Aug 5, 2014, 7:45:15 PM8/5/14
to jackso...@googlegroups.com
Hmm, did you try 'as class' without custom subtype resolver? It would seem unnecessary to override default one, if you are letting class name be used.

-+ Tatu +-

Jörg Hohwiller

unread,
Aug 19, 2014, 4:48:35 AM8/19/14
to jackso...@googlegroups.com
I am facing a very similar problem.

1. The transfer-objects that shall be mapped from JSON by jackson are generated and also can not have a dependency to jackson for technical reasons.
2. We have a showcase scenario where we have an AngularJS client posting JSON data that is supposed to work both with a Java Backend using jackson as well as with a .NET Backend with a totally different technology stack. Using class information is simply not suitable here. We want to have the JSON API stable and independent from the technical implementation.

Therefore I am heavily looking for any way how to use @type with a symbolic name and find a way to configure jackson e.g. giving a Map<String, Class> (bidirectional mapping from symbolic type name to the java class used as implementation that shall be instantiated and populated for unmarshalling). Do you have any kind of hint for us how to solve this with jackson? The TypeResolverBuilder should be the right place but currently I had the problem that I did not yet get the philosophy of jacksons internals and was more fighting in the code than harmonizing towards a solution...

Thank you so much
  Jörg

Jörg Hohwiller

unread,
Aug 19, 2014, 10:31:23 AM8/19/14
to jackso...@googlegroups.com

Tatu Saloranta

unread,
Aug 19, 2014, 7:13:25 PM8/19/14
to jackso...@googlegroups.com
Module interface (via SimpleModule) has a way of registering subtypes, along with optional name definitions; SimpleModule.registerSubtypes(...).
That should allow specifying type name to class mappings.

There is still need to indicate that specific (sub)type is polymorphic one; this can be done either using mix-in annotations, or by custom AnnotationIntrospector, which can replace/add handling to discover equivalent of @JsonTypeInfo form other sources.
Use of custom (Jackson)AnnotationIntrospector is the standard way of implementing non-annotations-based mechanisms for configuration; it may seem non-intuitive (ok, it IS non-intuitive :), due to naming.
But think of it as "ConfigurationForBeanTypesIntrospector" and it may make more sense -- default one just happens to only use annotations as the source.

-+ Tatu +-



--

Jörg Hohwiller

unread,
Aug 26, 2014, 6:08:06 AM8/26/14
to jackso...@googlegroups.com
Hi Tatu,

thanks for your response.
First of all sorry, but it seems I am having more fundamental problems with jackson and need more info.
It already starts with confusion that I include this dependency for using it with CXF:
 <groupId>com.fasterxml.jackson.jaxrs</groupId>  
 <artifactId>jackson-jaxrs-json-provider</artifactId>  
 <version>2.2.3</version>

Then I end up with most classes being duplicated in org.codehaus.jackson and in com.fasterxml.jackson and I do not really understand this mess. Is this already the source of the problem or is this kind of "normal"?
I also tried with org.codehaus.jackson:jackson-jaxrs:1.9.13 but this also has dependencies to com.fasterxml.jackson artifacts...
However, CXF requires an instance of ObjectMapper from org.codehaus.jackson so I assume that I have to use the code from this namespace.
In that case your suggestion does not work as SimpleModule has no method "registerSubtypes" as this only exists in the com.fasterxml.jackson variant.
I can only do mapper.getSubtypeResolver() and then "registerSubtypes" on that but this does not take an array or varargs and I am not sure if I should invoke it multiple times for each subtype.

In the end I always get this one:

org.codehaus.jackson.map.JsonMappingException: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class org.example.AbstractSuperclass

at [Source: org.apache.cxf.transport.http.AbstractHTTPDestination$1@79c6d8; line: 1, column: 1]

                at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163) ~[jackson-mapper-asl-1.9.9.jar:1.9.9]

I already consulted these guides:

http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

http://stackoverflow.com/questions/10329706/json-deserialization-into-another-class-hierarchy-using-jackson (Mixin approach is also interesting for the generated transfer objects that I cannot manipulate)

Any help is appreciated.

Thanks Jörg

Tatu Saloranta

unread,
Aug 27, 2014, 2:39:44 PM8/27/14
to jackso...@googlegroups.com
Ok, first things first: org.codehaus.jackson is used by Jackson 1.x; and com.fasterxml.jackson by Jackson 2.x. These two versions are not binary compatible, and co-exist in different Java packages. Projects may choose to use both, in most cases it is best to limit to using just one.
In case of frameworks (like CXF) choice may be already made for you.
One source of confusion is that names of main objects in both packages are usually same.

JAX-RS provider builds on either 1.x or 2.x Jackson core components, and follows same package name convention.
So you are correct in that if CXF uses 1.x, your code needs to use it as well.

You should however be able to use latest 1.9 version, that is, 1.9.13, and it should not have any dependencies to Jackson 2.x components (com.fasterxml.jackson namespace).

For 1.x it should still be possible to register subtypes via module interface, but maybe convenience methods were not added in SimpleModule. You should however be able to register these either via context in callback that Module gets; or, directly via ObjectMapper.
It has been quite a while since I look into 1.x codebase so I do not remember names of calls -- if you have hard time finding them I can go over javadocs/source to see what the hooks are.

I don't know what the timeline is for CXF to upgrade, but I really hope they can get to use Jackson 2.x; at this point all new functionality and improvements only go in 2.x.
1.x will only get critical bug fixes; and last release was more than a year ago.

-+ Tatu +-

Jörg Hohwiller

unread,
Sep 15, 2014, 5:37:04 AM9/15/14
to jackso...@googlegroups.com
Hi Tatu,

thanks for the clarification that was what I already guessed. I managed to eliminate all codehaus (1.x) dependencies and references and it turned out that there was a mixture of 1.x and 2.x code causing the problem.
I use mixin approach to define the annotations in an inner config class and mix it into the generated classes.
Works like a charm.

Also I am working on a generic datatype support in an OSS project (see https://github.com/m-m-m/mmm/blob/master/mmm-util/mmm-util-core/src/main/java/net/sf/mmm/util/lang/api/DatatypeDescriptorManager.java and https://github.com/m-m-m/mmm/blob/master/mmm-util/mmm-util-core/src/test/java/net/sf/mmm/util/lang/base/datatype/descriptor/DatatypeDescriptorManagerTest.java) and will write a generic bridge for JAXB (XMLAdapter), Jackson (JsonDeserializer and JsonSerializer) and Hibernate (UserType) based on this.

Thanks for your support...

Kind regards
  Jörg

Tatu Saloranta

unread,
Sep 15, 2014, 1:08:56 PM9/15/14
to jackso...@googlegroups.com
On Mon, Sep 15, 2014 at 2:37 AM, Jörg Hohwiller <joerg.h...@googlemail.com> wrote:
Hi Tatu,

thanks for the clarification that was what I already guessed. I managed to eliminate all codehaus (1.x) dependencies and references and it turned out that there was a mixture of 1.x and 2.x code causing the problem.
I use mixin approach to define the annotations in an inner config class and mix it into the generated classes.
Works like a charm.

Also I am working on a generic datatype support in an OSS project (see https://github.com/m-m-m/mmm/blob/master/mmm-util/mmm-util-core/src/main/java/net/sf/mmm/util/lang/api/DatatypeDescriptorManager.java and https://github.com/m-m-m/mmm/blob/master/mmm-util/mmm-util-core/src/test/java/net/sf/mmm/util/lang/base/datatype/descriptor/DatatypeDescriptorManagerTest.java) and will write a generic bridge for JAXB (XMLAdapter), Jackson (JsonDeserializer and JsonSerializer) and Hibernate (UserType) based on this.

Thanks for your support...

Very cool, thank you for sharing details of the use case.

-+ Tatu +-
Reply all
Reply to author
Forward
0 new messages