Suzan Cioc
Suzan Cioc

Reputation: 30097

How to delegate to default deserialization in custom deserializer in Jackson?

Suppose I am writing custom serialization for some class, but would like to process one of its field with default methods.

How to do that?

While serializing we have JsonGenerator#writeObjectField().

But what is corresponding method for deserialization?

Regard the code below:

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import java.io.IOException;
import java.util.Objects;

public class TryDelegate {

   public static class MyOuterClassSerializer extends JsonSerializer<MyOuterClass> {

      @Override
      public void serialize(MyOuterClass value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
         gen.writeStartObject();

         gen.writeObjectField("inner", value.getInner());

         gen.writeEndObject();
      }
   }

   public static class MyOuterClassDeserializer extends JsonDeserializer<MyOuterClass> {
      @Override
      public MyOuterClass deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {


         MyOuterClass ans = new MyOuterClass();

         JsonToken token;

         token = p.getCurrentToken();
         if( token != JsonToken.START_OBJECT ) {
            throw new JsonParseException("Start object expected", p.getCurrentLocation());
         }

         if( !"inner".equals(p.nextFieldName() ) ) {
            throw new JsonParseException("'inner; field expected", p.getCurrentLocation());
         }

         MyInnerClass inner = null;// how to desrialize inner from here with default processing???
         ans.setInner(inner);

         token = p.nextToken();
         if( token != JsonToken.END_OBJECT ) {
            throw new JsonParseException("End object expected", p.getCurrentLocation());
         }

         return ans;

      }
   }

   public static class MyInnerClass {
      private int value;

      public int getValue() {
         return value;
      }

      public void setValue(int value) {
         this.value = value;
      }

      @Override
      public String toString() {
         return "{\"value\":" + value + "}";
      }
   }

   @JsonDeserialize(using = MyOuterClassDeserializer.class)
   @JsonSerialize(using = MyOuterClassSerializer.class)
   public static class MyOuterClass {

      private MyInnerClass inner;

      public MyInnerClass getInner() {
         return inner;
      }

      public void setInner(MyInnerClass inner) {
         this.inner = inner;
      }

      @Override
      public String toString() {
         return "{\"inner\":" + Objects.toString(inner) + "}";
      }
   }

   public static void main(String[] args) throws IOException {

      ObjectMapper mapper = new ObjectMapper();
      String string;

      MyInnerClass inner = new MyInnerClass();
      inner.setValue(12);

      MyOuterClass outer = new MyOuterClass();
      outer.setInner(inner);

      string = mapper.writeValueAsString(outer);
      System.out.println(string);

      MyOuterClass outer2 = mapper.readValue(string, MyOuterClass.class);
      System.out.println(outer2); // inner was not deserialized


   }
}

How to implement MyOuterDeserializer?

Upvotes: 21

Views: 11902

Answers (2)

jgoyer
jgoyer

Reputation: 73

Saved my bacon, thanks for this answer.

Basic technique here is to loop through the tokens and check for token of type FIELD_NAME with the name of the object you are looking for. Then advance past the field name (p.nextToken) and give the JsonParser and class type you want to the context. Works!

JsonToken token = p.currentToken();
while (token != null) {
    if (p.currentToken().equals(JsonToken.FIELD_NAME) && 
                              p.getText().equals("targetField")) {
       p.nextToken();
       TargetType target = 
                 deserializationContext.readValue(p,Targettype.class);
    }
}

Upvotes: 0

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279880

The DeserializationContext offers these tools.

After checking the field name for "inner", move to the next token, the beginning of the JSON object and use the DeserializationContext to deserialize the JSON object into a MyInnerClass object.

if (!"inner".equals(p.nextFieldName())) {
    throw new JsonParseException("'inner; field expected", p.getCurrentLocation());
}
p.nextToken(); // consumes the field name token

MyInnerClass inner = ctxt.readValue(p, MyInnerClass.class);

The javadoc states

Convenience method that may be used by composite or container deserializers, for reading one-off values contained (for sequences, it is more efficient to actually fetch deserializer once for the whole collection).


Careful while using the DeserializationContext. Don't try to recursively deserialize types for which you have have registered custom deserializers.

Upvotes: 10

Related Questions