Handle All Subtypes with Custom Deserializer Without Explicit Registration

564 views
Skip to first unread message

Kamal Advani

unread,
Sep 3, 2014, 9:02:14 PM9/3/14
to jackso...@googlegroups.com
Hello,

I have a supertype (interface to be specific, but hopefully that should not matter) for which all extended subtypes should use a custom deserializer. My restrictions:

* No type info in the JSON representation.
* Preferably it can be done in an annotation free way, or the annotation can be limited to the supertype interface. Even this is less than ideal, but I can live with it.
* I don't wish to explicitly register subtypes.
* Must apply to collections containing those subtypes too.

At the moment, I am not doing this in a polymorphic way, I simply annotate each field that is a known subtype of this base type to use the custom deserializer (@JsonDeserialize(using = ....)). Obviously, this gets pretty noisy after a while.

Any hints on what hooks I need to achieve this? I've seen references to type resolvers and all, but not really sure what combo of incantations I need exactly.

The following is a more detailed description, as more context, of what I am doing:

- Incoming JSON is a web link representation that needs to be resolved to an entity on the server.
- The link's decoding is handled entirely by my (one) custom deserialiser, this is purely domain-specific and is working.

{
    "foo": { "href": "/foo/1" } // resolve to an instance of type Foo (which will extends some supertype)
    "bar": { "href": "/bar/1" } // ditto, bar instance
}

My JSON request object will be:

class Request {
    Foo foo; // not a link! Must be automatically resolved to Foo
    Bar bar; // ditto, resolve to Bar instance 
}

// App entities
class Link { String href }
interface Super {}
class Foo implements Super {}
class Bar implements Super {}

At this time I have:

class Request {
    @JsonDeserialize(using = CustomDeserializer.class)
    Foo foo;
    @JsonDeserialize(using = CustomDeserializer.class)
    Bar bar;
}

CustomDeserialiser first does a readValue(..., Link), then decodes it to Foo. The custom deserialiser is registered via a SimpleModule against Foo's super type. But this within itself does not enable polymorphic handling.

Thanks all.



Tatu Saloranta

unread,
Sep 12, 2014, 12:17:29 AM9/12/14
to jackso...@googlegroups.com
On Thu, Sep 4, 2014 at 1:02 AM, Kamal Advani <kamal....@gmail.com> wrote:
Hello,

I have a supertype (interface to be specific, but hopefully that should not matter) for which all extended subtypes should use a custom deserializer. My restrictions:

* No type info in the JSON representation.

This will not work with functionality Jackson offers out of the box -- there has to be some kind of type information, for polymorphic type resolution to work.
You may need to write custom deserializer that fully handles deserialization, including type resolution, and it probably can not make use of existing Type(De)Serializer support.

It may still be possible to use some delegation; for example, your deserializer may bind incoming JSON into JsonNode (jsonParser.readTree()), and then handle type detection from tree manually, and finally delegating to subtype deserializer (which may be out-of-the-box one). It gets more involved but should be doable, and is something many developers have done I think. Perhaps others can share their usage and ideas.
 
* Preferably it can be done in an annotation free way, or the annotation can be limited to the supertype interface. Even this is less than ideal, but I can live with it.
* I don't wish to explicitly register subtypes.
* Must apply to collections containing those subtypes too.

This should be the case, regardless of whether using out-of-the-box support, or custom deserializers; container deserializers delegate handling appropriately.

-+ Tatu +-

Kamal Advani

unread,
Sep 12, 2014, 1:19:19 AM9/12/14
to jackso...@googlegroups.com


On Friday, 12 September 2014 14:17:29 UTC+10, Tatu Saloranta wrote:
On Thu, Sep 4, 2014 at 1:02 AM, Kamal Advani <kamal....@gmail.com> wrote:
Hello,

I have a supertype (interface to be specific, but hopefully that should not matter) for which all extended subtypes should use a custom deserializer. My restrictions:

* No type info in the JSON representation.

This will not work with functionality Jackson offers out of the box -- there has to be some kind of type information, for polymorphic type resolution to work.
You may need to write custom deserializer that fully handles deserialization, including type resolution, and it probably can not make use of existing Type(De)Serializer support.


Some examples of these would be really handy. Jackson is awesomely configurable, but it's difficult to find comprehensive examples of extension/plugin points. I would help, if there was a starting point. Let me know.

Nevertheless, a big thanks for an awesome product.



It may still be possible to use some delegation; for example, your deserializer may bind incoming JSON into JsonNode (jsonParser.readTree()), and then handle type detection from tree manually, and finally delegating to subtype deserializer (which may be out-of-the-box one). It gets more involved but should be doable, and is something many developers have done I think. Perhaps others can share their usage and ideas.
 


At this time, I have taken a similar approach: I realised that to do custom deserialisation I have collected the type information already (basically the types/classes are annotated, collection done using the very handy Reflections (https://code.google.com/p/reflections/) library). So using the same source, I registered each collected type against the custom deserialiser.

for each type {
    module.addDeserializer(type, deserialiser)
}

Effectively this is what I want, and I think the symmetry is appropriate, it is exactly those types that need to be handled by the deserialiser, and nothing else. They are of course polymorphic by way of domain concerns (which is why my head was in this space)... but given the type info I already collected, the polymorphism, as far as Jackson is concerned anyway, is irrelevant.



 
* Preferably it can be done in an annotation free way, or the annotation can be limited to the supertype interface. Even this is less than ideal, but I can live with it.
* I don't wish to explicitly register subtypes.
* Must apply to collections containing those subtypes too.

This should be the case, regardless of whether using out-of-the-box support, or custom deserializers; container deserializers delegate handling appropriately.


Indeed, good to know that nothing special is needed to handle collections even for extensions. :-)

Thanks again.

Tatu Saloranta

unread,
Sep 15, 2014, 1:34:07 PM9/15/14
to jackso...@googlegroups.com
On Thu, Sep 11, 2014 at 10:19 PM, Kamal Advani <kamal....@gmail.com> wrote:


On Friday, 12 September 2014 14:17:29 UTC+10, Tatu Saloranta wrote:
On Thu, Sep 4, 2014 at 1:02 AM, Kamal Advani <kamal....@gmail.com> wrote:
Hello,

I have a supertype (interface to be specific, but hopefully that should not matter) for which all extended subtypes should use a custom deserializer. My restrictions:

* No type info in the JSON representation.

This will not work with functionality Jackson offers out of the box -- there has to be some kind of type information, for polymorphic type resolution to work.
You may need to write custom deserializer that fully handles deserialization, including type resolution, and it probably can not make use of existing Type(De)Serializer support.


Some examples of these would be really handy. Jackson is awesomely configurable, but it's difficult to find comprehensive examples of extension/plugin points. I would help, if there was a starting point. Let me know.

Nevertheless, a big thanks for an awesome product.

Yes, documentation is a big challenge. "With great power [there should] come great documentation"...
 



It may still be possible to use some delegation; for example, your deserializer may bind incoming JSON into JsonNode (jsonParser.readTree()), and then handle type detection from tree manually, and finally delegating to subtype deserializer (which may be out-of-the-box one). It gets more involved but should be doable, and is something many developers have done I think. Perhaps others can share their usage and ideas.
 


At this time, I have taken a similar approach: I realised that to do custom deserialisation I have collected the type information already (basically the types/classes are annotated, collection done using the very handy Reflections (https://code.google.com/p/reflections/) library). So using the same source, I registered each collected type against the custom deserialiser.

for each type {
    module.addDeserializer(type, deserialiser)
}

Effectively this is what I want, and I think the symmetry is appropriate, it is exactly those types that need to be handled by the deserialiser, and nothing else. They are of course polymorphic by way of domain concerns (which is why my head was in this space)... but given the type info I already collected, the polymorphism, as far as Jackson is concerned anyway, is irrelevant.



 
* Preferably it can be done in an annotation free way, or the annotation can be limited to the supertype interface. Even this is less than ideal, but I can live with it.
* I don't wish to explicitly register subtypes.
* Must apply to collections containing those subtypes too.

This should be the case, regardless of whether using out-of-the-box support, or custom deserializers; container deserializers delegate handling appropriately.


Indeed, good to know that nothing special is needed to handle collections even for extensions. :-)

Thanks again.

You are welcome; glad you were able to make things work, even if it was some work.

-+ 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.

Reply all
Reply to author
Forward
0 new messages