0
votes

I have a light wrapper class around a complex class for which I needed to write a custom Jackson JSON deserializer. The wrapper class is simple and only contains a String, a Date, and my complex object as properties. Does Jackson automatically apply a simple deserializer to the wrapper and my custom deserializer to my complex object? The custom deserializer works by itself. But when I try to serialize the wrapper Jackson throws a Nullpointer Exception. I must be missing something conceptual. Do I have to register another serializer with my module in addition to my custom deserializer?

java.lang.NullPointerException
    at org.codehaus.jackson.impl.ReaderBasedParser._skipWSOrEnd(ReaderBasedParser.java:1477)
    at org.codehaus.jackson.impl.ReaderBasedParser.nextToken(ReaderBasedParser.java:368)
    at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:690)
    at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:580)
    at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2732)
    at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1863)
    at com.newoak.noc.curve.model.tests.ModelParamsTest.deserializeGraph(ModelParamsTest.java:100)
    at com.newoak.noc.curve.model.tests.ModelParamsTest.testSerializationDeserialization(ModelParamsTest.java:113)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)

Trying to deserialize

public ModelParamGraph deserializeGraph(String json) throws JsonGenerationException, JsonMappingException, IOException
    {
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule testModule = new SimpleModule("MyModule", new Version(1, 0, 0, null));
        testModule.addSerializer(new SpaceJsonSerializer());
        testModule.addDeserializer(Space.class, new SpaceJsonDeserializer());
        mapper.registerModule(testModule);

        ModelParamGraph space = mapper.readValue(json, ModelParamGraph.class);

        return space;
    }

Wrapper

public class ModelParamGraph
{
    public String source;

    public Date date;

    @JsonSerialize(using=SpaceJsonSerializer.class)
    @JsonDeserialize(using=SpaceJsonDeserializer.class)
    public Space<TModelParam> paramSpace;

    public ModelParamGraph()
    {
    }

    public ModelParamGraph(String source, Date date)
    {
        setSource(source);
        setDate(date);

        setParamSpace(new Space<TModelParam>());
    }
    //getters and setters
}
1
I would recommend you slim down the classes involved and give us a snippet of code that would reproduce your problem.Sotirios Delimanolis
The Space class deserializes fine, and i can even step through the deserializer when jackson calls it but then the nullpointer exception follows. The default constructor of the wrapper gets called prior to the deserializerbjoern
I cant find any examples online of how to deal with custom deserializers for nested classes.bjoern

1 Answers

1
votes

If you can modify the class being wrapped, then you can use Jackson's @JsonView annotation, here is the full tutorial.

Step 1: create interfaces like so:

public class MyJsonViews {
    public static class Small { }
    public static class Medium extends Small { }
    public static class Large extends Medium { }
}

Step 2: annotate properties (or methods) in your POJOs:

public class Wrapper {
    @JsonView(MyJsonViews.Small.class)
    private String name;

    @JsonView(MyJsonViews.Medium.class)
    private Wrapped wrapped;
    // getters and setters
}

public class Wrapped {
   @JsonView(MyJsonViews.Small.class)
   private String someField;

   @JsonView(MyJsonViews.Medium.class)
   private String anotherField;
  // getters and setters
}

Step 3: serialize using the new views:

ObjectMapper objectMapper = new ObjectMapper();
// important: this excludes all fields without @JsonView from being serialized
objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
String json = objectMapper.writerWithView(SMyJsonViews.mall.class).writeValueAsString(wrapper);

Your JSON will now contain just the fields annotated with @JsonView(MyJsonViews.Small.class).

If you cannot modify the class being wrapped, then you could use the Filter approach, described here.