Search code examples
javajacksondeserializationjson-deserializationjackson2

Jackson MyCustomDeserializer super.deserialize(parser, dc, intoValue); not working


I'm running Spring Boot with Jackson 2.10.3.

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.10.3</version>
</dependency>      
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>2.10.3</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.10.3</version>
</dependency>

My input value is a json representation of an serialized object, in my case FC.java When creating the Mapper a new CustomerFCDeserializer is registered.

  mapper = new ObjectMapper();
  SimpleModule module = new SimpleModule();
  module.addDeserializer(FC.class, new CustomFCDeserializer(fcFactory));
  mapper.registerModule(module);

The CustomFCDeserializer is inteded to initialize an FC object the serialized object should be deserialized into. fc = super.deserialize(parser, dc, fc);

Normally i would just deserialize it, but in this specific case the default constructor can't be called, instead the object needs some other initializiation as done by FcFactory. This way also serialized fields will be set in the object returned by the factory.

public class CustomFCDeserializer extends StdDeserializer<FC> {
    private final FCFactory fcFactory;

    public CustomFCDeserializer(FCFactory fcFactory) {
        super(FC.class);
        this.fcFactory = fcFactory;
    }

    @Override
    public FC deserialize(JsonParser parser, DeserializationContext dc) throws IOException, JsonProcessingException {
        ObjectCodec codec = parser.getCodec();
        JsonNode node = codec.readTree(parser);
        JsonNode idNode = node.get("id");
        long id = idNode.asLong(0);
        FC fc = fcFactory.getFCById(id); //<!--- create my object
        //set values on prev created fc object
        fc = super.deserialize(parser, dc, fc); //<!-- pollute it 
        return fc;
    }
}

Running this example will recursivly call deserialize, super.deserialize and finally deserialize again which fails as structure changed.

java.lang.NullPointerException: null
    at objectMapper.CustomFcDeserializer.deserialize(CustomFcDeserializer.java:38) ~[classes/:na]
    at objectMapper.CustomFcDeserializer.deserialize(CustomFcDeserializer.java:16) ~[classes/:na]
    at com.fasterxml.jackson.databind.JsonDeserializer.deserialize(JsonDeserializer.java:129) ~[jackson-databind-2.9.0.jar:2.9.0]
    at objectMapper.CustomFcDeserializer.deserialize(CustomFcDeserializer.java:51) ~[classes/:na]
    at objectMapper.CustomFcDeserializer.deserialize(CustomFcDeserializer.java:16) ~[classes/:na]
[...]

Is there a better way than handeling this issue by a bunch of if statements? Or is the a more intelligent way to pollute and existing object? deserialize(JsonParser p, DeserializationContext ctxt, T intoValue) seems be fine as it takes an intoValue, but im deserializing an object, not a collection nor map. Btw and my object isn't and doesnt have immutable members.

com.​fasterxml.​jackson.​databind.​JsonDeserializer
public T deserialize(JsonParser p, DeserializationContext ctxt, T intoValue) throws IOException
Alternate deserialization method (compared to the most commonly used, deserialize(JsonParser, DeserializationContext)), which takes in initialized value instance, to be configured and/or populated by deserializer. Method is not necessarily used (or supported) by all types (it will not work for immutable types, for obvious reasons): most commonly it is used for Collections and Maps. It may be used both with "updating readers" (for POJOs) and when Collections and Maps use "getter as setter".
Throws: IOException

Solution

  • went to deep into overwriting mapper and deserializer, simply do

    Map<String, Object> fcInput = dfe.getArgument("fcInput");
    FC fcInput = mapper.convertValue(fcInput, FC.class);
    String fcString = mapper.writeValueAsString(fcInput);
    
    long fcId = fcInput.getId();
    FC existingFc = fcFactory.getFC(fcId);
    
    ObjectMapper om = new ObjectMapper();
    ObjectReader or = om.readerForUpdating(existingFc);
    FC updatedFc = or.readValue(fcString);