Call and apply custom deserializer under conditions

1,652 views
Skip to first unread message

Vianney DEPAUW

unread,
Dec 6, 2015, 1:42:21 PM12/6/15
to jackson-user
Hi everyone,

I'm looking of how two apply "normal deserialization" even if a custom deserializer has been provided (which by default will be always called). By normal deserialization I mean like if no custom deserializer has been provided.

Here is a complete example case with the problem I get.
This example has two simple beans.

public class Building {
   
private Zone zone;
   
// Getters and Setters
}


public class Zone {
   
private String firstRoom;
   
private String secondRoom;
   
// Getters and Setters
}


So the deserialization I'm trying to implement must have the following goal:
- If a call mapper.readValue(jsonStr, Building.class) I don't want to call my custom deserializer.
- But if a call mapper.readValue(jsonStr, Zone.class) I want to call my custom deserializer (or at least say "apply normal bean deserialization" into).


addDeserializer(Zone.class, new JsonDeserializer<Zone>() {
   
@Override
   
public Zone deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
       
// If zone has a parent, perform custom deserialization
       
if (p.getCurrentValue() == null) {
           
Zone zone = new Zone();
            zone
.setFirstRoom("Hello");
            zone
.setSecondRoom("World");
           
return zone;
       
}


       
// Otherwise apply normal bean deserialization...
       
// ... but infinite loop here
       
return p.getCodec().readValue(p, Zone.class);
   
}
});


So to resolve this I added a condition in my custom deserializer to check if I deserialize a zone included in a building, or directly a zone (by checking if the zone in the JSON has a parent property).
Everything works well if a want to apply my custom deserializer, but I get an infinite loop if I don't want to apply my custom deserializer, which is a normal things with my code.

How can I do a custom deserializer that is called only under some conditions ?

Tatu Saloranta

unread,
Dec 6, 2015, 1:50:53 PM12/6/15
to jackson-user
Typically this is handled by registering `BeanDeserializerModifier`, overriding method `modifyDeserializer`. Doing this will give you access to the default deserializer, and you can construct your own deserializer to wrap it (or leave it as-is, if that makes sense).
But when deserializing, you can then figure out whether to handle the case yourself, or delegate.
This is somewhat cumbersome, but figuring out how to handle recursive/cyclic nature of things otherwise is surprisingly difficult problem to tackle at lib/framework level; partly because there isn't really a clear-cut concept of default deserializer as any module can add their own handlers, and every type down to java.lang.Object can have custom overrides.

Anyway, I think this would work for your case.

-+ Tatu +-

ps. Nice use of `getCurrentValue`; it is not widely known or used yet, but this is a good example of how it might be used -- well spotted.


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

Vianney DEPAUW

unread,
Dec 7, 2015, 5:37:57 AM12/7/15
to jackson-user
Thanks for this solution.

The problem is when I'm using BeanDeserializerModifier I'm not able to know if the current value I deserialize is the root of my JSON or a part of a subnode.
This information was known thanks to the JsonParser (and getCurrentValue()) that I don't retrieve from the modifier.

P.S. : I did a little mistake in my code using getCurrentValue(). If you try it you will see that getCurrentValue() will always return null.
Just add p.getCodec().readTree(p) before calling getCurrentValue() to fix it.

Tatu Saloranta

unread,
Dec 7, 2015, 1:47:32 PM12/7/15
to jackson-user
No, you would not get a call to `BeanDeserializerModifier` at the time deserialization happens, and there is no context. Rather it allows you to wrap up default deserializer in a way that later on, during deserialization, allows you to conditionally delegate to the default implementation.
BeanDeserializerModifier only gets called once, when instantiating necessary deserializer for given type: result is cached and used for other properties of that type. This is necessary to keep performance acceptable: lookups and traversal are rather expensive.

-+ Tatu +-

Vianney DEPAUW

unread,
Dec 7, 2015, 3:50:59 PM12/7/15
to jackson-user
In that case this is not seems to be what I'm looking for. I need to check the condition at the time of the deserialization.
Maybe can I get the default BeanDeserializer in cache from my custom deserializer and call the deserialize method myself ?

Tatu Saloranta

unread,
Dec 7, 2015, 4:14:38 PM12/7/15
to jackson-user
I wish you actually read what I said and thought it through, as that is pretty much the way to do what you are trying to do.

If you register a custom (de)serializer directly, there is no access to Bean(De)Serializer since one is not constructed (because a custom (de)serializer is located first).

-+ Tatu +-

Reply all
Reply to author
Forward
0 new messages