Calling default deserializers from customer deserializer

3,366 views
Skip to first unread message

Vladimir

unread,
Aug 18, 2017, 4:32:56 PM8/18/17
to jackson-user

I implement the ResolvableDeserializer within my custom deserializer, which gives me the reference to the default JsonDeserializer<?>.
Id like to invoke the default deserializer on the "Jail" JsonNode to return to me a Jail object

public class BadStudentDeserializer extends StdDeserializer<BadStudent>
   
implements ResolvableDeserializer{
 
private static final long serialVersionUID = 1L;
 
private JsonDeserializer<?> defaultDeserializer;


 
public BadStudentDeserializer(JsonDeserializer<?> defaultDeserializer) {
   
super(BadStudent.class);
   
this.defaultDeserializer = defaultDeserializer;
 
}
 
 
@Override
 
public BadStudent deserialize(JsonParser p, DeserializationContext ctxt)
     
throws IOException, JsonProcessingException {
   
JsonNode jsonTree = p.getCodec().readTree(p);
   
   
String id = jsonTree.get("id").asText();
   
String weapon = jsonTree.get("weapon").asText();
   
JsonNode jailNode = jsonTree.get("jail");
   
JsonParser jailNodeParser = jailNode.traverse();
   
//advance it as per documentation
    jailNodeParser
.nextToken();
   
//this doesnt work
   
Jail jail = (Jail)defaultDeserializer.
        deserialize
(jailNodeParser, ctxt);
   
return new BadStudent(id, weapon, jail);
 
}


 
@Override
 
public void resolve(DeserializationContext ctxt) throws JsonMappingException {
   
((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
   
 
}

 


How do I provide the defaultDeserializer a clue as to what class TYPE a tree node is, so that it can pick up the right deserializer or that treeNode?

Hope I am able to explain myself clearly.



Tatu Saloranta

unread,
Aug 18, 2017, 4:38:24 PM8/18/17
to jackson-user
`JsonNode` has methods (`isObject()`, `isArray()`), if you are
interested in type of json node read.
But just looking at naming, it seems odd that you would delegate
contents of `jail` to default POJO
deserializer that would be used for `BadStdudent`?

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

Vladimir

unread,
Aug 18, 2017, 5:58:19 PM8/18/17
to jackson-user
Tatu,

Thank you for your response.

My goal was to delegate some deserialization inside of my custom deserializer to the Jacksons default deserializer, which is aware of other registered deserializers for a given type.
So in my example, I wanted to pass on the Jail treenode the Jacksons default deserializer and let Jackson figure out which other registered deserializer to use. 

Tatu Saloranta

unread,
Aug 18, 2017, 6:28:40 PM8/18/17
to jackson-user
On Fri, Aug 18, 2017 at 2:58 PM, Vladimir <vsuts...@gmail.com> wrote:
> Tatu,
>
> Thank you for your response.
>
> My goal was to delegate some deserialization inside of my custom
> deserializer to the Jacksons default deserializer, which is aware of other
> registered deserializers for a given type.
> So in my example, I wanted to pass on the Jail treenode the Jacksons default
> deserializer and let Jackson figure out which other registered deserializer
> to use.

Then you need to find the deserializer through
`DeserializationContext`, giving target type.
This can be done either from `deserialize()`, or from initial call to
`resolve()`.

-+ Tatu +-

Vladimir

unread,
Aug 18, 2017, 6:46:45 PM8/18/17
to jackson-user
Tatu,

That makes sense, I did not realize the context provided methods for that.
What did you mean by:
This can be done either from `deserialize()`, or from initial call to 
`resolve()`. 


Now that I am looking at the java docs I see:  

JsonNode jailNode = jsonTree.get("jail");

JsonParser jailNodeParser = jailNode.traverse();

Jail jail = ctxt.readValue(jailNodeParser, Jail.class);


Is that what you meant?

Tatu Saloranta

unread,
Aug 18, 2017, 7:32:59 PM8/18/17
to jackson-user
On Fri, Aug 18, 2017 at 3:46 PM, Vladimir <vsuts...@gmail.com> wrote:
> Tatu,
>
> That makes sense, I did not realize the context provided methods for that.
> What did you mean by:
> This can be done either from `deserialize()`, or from initial call to
> `resolve()`.

Just that either you can look up deserializer every time it is needed
(with some overhead),
or, if you know types for which they are needed, just once in
`resolve()` (or, possibly from
`createContextual()` -- depends on whether there could be cyclic dependencies).

>
> Now that I am looking at the java docs I see:
>
> JsonNode jailNode = jsonTree.get("jail");
>
> JsonParser jailNodeParser = jailNode.traverse();
>
> Jail jail = ctxt.readValue(jailNodeParser, Jail.class);
>
>
> Is that what you meant?

No I mean that there are methods like `findContextualValueDeserializer()` and
`findNonContextualValueDeserializer()` (difference being whether you
have access to
`BeanProperty`, which is made available in `createContextual()`, but not during
`deserialize()`).
And with that you can delegate to deserializer you get.

Vladimir

unread,
Aug 20, 2017, 4:48:27 PM8/20/17
to jackson-user

Thank you for the response once again.

As you suggested I am using "findNonContextualValueDeserializer" to locate deserializer for a given type I expect.

JsonNode eventNode = jsonTree.get("event");

ctxt

.findNonContextualValueDeserializer(ctxt.constructType(Event.class))

.deserialize(eventNode.traverse(parser.getCodec()));


In my case event is an abstract class, I wont know exactly what concrete implementation its going to be unless I look at the eventNode and extract @class property which contains the fully qualified java class name. 

My question is:
Will I have to parse out the type property specifying concrete class first? Then construct a JavaType from it with, and then look for the custom deserializer using the findNonContextualValueDeserializer(javaType).

Ultimately I am trying to achieve the same dispatching behavior in deserializers as Jackson has in the custom serializers. In the serializer example below all I have to do is gen.WriteObjectField("fieldName", someObject), and  Jackson locates the custom serializers I have declared somewhere else for the someObject. 
 @Override
 
public void serialize(EventMessage value, JsonGenerator gen,
     
SerializerProvider provider) throws IOException {

    gen
.writeStartObject();
    gen
.writeObjectField("event", value.getPayLoad());
    gen
.writeObjectField("headers", value.getHeaders());
    gen
.writeEndObject();
 
}

 @Override
 
public void serialize(EventMessage value, JsonGenerator gen,
     
SerializerProvider provider) throws IOException {

    gen
.writeStartObject();
    gen
.writeObjectField("event", value.getPayLoad());
    gen
.writeObjectField("headers", value.getHeaders());
    gen
.writeEndObject();
 
}

events.png

Tatu Saloranta

unread,
Aug 24, 2017, 12:17:14 AM8/24/17
to jackson-user
On Sun, Aug 20, 2017 at 1:48 PM, Vladimir <vsuts...@gmail.com> wrote:
>
> Thank you for the response once again.
>
> As you suggested I am using "findNonContextualValueDeserializer" to locate
> deserializer for a given type I expect.
>
> JsonNode eventNode = jsonTree.get("event");
>
> ctxt
>
> .findNonContextualValueDeserializer(ctxt.constructType(Event.class))
>
> .deserialize(eventNode.traverse(parser.getCodec()));
>
>
> In my case event is an abstract class, I wont know exactly what concrete
> implementation its going to be unless I look at the eventNode and extract
> @class property which contains the fully qualified java class name.
>
> My question is:
> Will I have to parse out the type property specifying concrete class first?
> Then construct a JavaType from it with, and then look for the custom
> deserializer using the findNonContextualValueDeserializer(javaType).

I am not sure I understand.

If you read the whole JSON sub-tree into `JsonNode`, you can extract
out class name,
construct `JavaType` from it (via TypeFactory, or if it's simple class
name, Class.forName()),
and pass that type? There is no need to bind it into other types in between.

But if and when you need to extract more than one typed value, you can
traverse `JsonNode`
tree and take further sub-trees. There is no need to try to make the
whole thing map anything unless doing
that simplifies handling.

>
> Ultimately I am trying to achieve the same dispatching behavior in
> deserializers as Jackson has in the custom serializers. In the serializer
> example below all I have to do is gen.WriteObjectField("fieldName",
> someObject), and Jackson locates the custom serializers I have declared
> somewhere else for the someObject.
> @Override
> public void serialize(EventMessage value, JsonGenerator gen,
> SerializerProvider provider) throws IOException {
>
> gen.writeStartObject();
> gen.writeObjectField("event", value.getPayLoad());
> gen.writeObjectField("headers", value.getHeaders());
> gen.writeEndObject();
> }
>
> @Override
> public void serialize(EventMessage value, JsonGenerator gen,
> SerializerProvider provider) throws IOException {
>
> gen.writeStartObject();
> gen.writeObjectField("event", value.getPayLoad());
> gen.writeObjectField("headers", value.getHeaders());
> gen.writeEndObject();
> }

Read and write sides are not symmetrical in behavior unfortunately,
but I think that is a natural property of things.
Deserialization is more difficult than serialization.

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