Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enable Jackson Deserialization of Empty Objects to Null

I was asked to change our jackson mapping configuration so that each empty object we deserialize (from JSON) is going to be deserialized as null.

The problem is that I'm struggling to do it, but without any luck. Here is a sample of our ObjectMapper configuration (and example):

ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, true);
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ISO_DATE_TIME));
javaTimeModule.addDeserializer(Instant.class, InstantDeserializer.INSTANT);
mapper.registerModule(javaTimeModule);
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
warmupMapper(mapper);

return mapper;

I thought about something like adding:

mapper.configure(
    DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);

but it just works on strings.

I'm afraid that using a custom deserializer will not help me, because I'm writing a generic (for all objects) mapper. So I probably need something like a delegator or a post process deserialization method.

So for json like "" or {} I expect to be converted to null in java (and not to empty string or Object instance).

like image 968
Aladin Avatar asked Oct 16 '22 12:10

Aladin


3 Answers

What is a empty object for you? A object with null value fields? A object with no fields? You can create a custom to check the nodes and deserialize how you want. I see no problem to use it in a generic way.

I did a little example:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.io.IOException;
import java.util.Objects;

public class DeserializerExample<T> extends StdDeserializer<T> {

    private final ObjectMapper defaultMapper;

    public DeserializerExample(Class<T> clazz) {
        super(clazz);
        defaultMapper = new ObjectMapper();
    }

    @Override
    public T deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
        System.out.println("Deserializing...");

        JsonNode node = jp.getCodec().readTree(jp);

        for (JsonNode jsonNode : node) {
            if (!jsonNode.isNull()) {
                return defaultMapper.treeToValue(node, (Class<T>) getValueClass());
            }
        }

        return null;
    }

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

        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addDeserializer(Person.class, new DeserializerExample(Person.class));
        mapper.registerModule(module);

        Person person = mapper.readValue("{\"id\":1, \"name\":\"Joseph\"}", Person.class);

        Person nullPerson = mapper.readValue("{\"id\":null, \"name\":null}", Person.class);

        System.out.println("Is null: " + Objects.isNull(person));
        System.out.println("Is null: " + Objects.isNull(nullPerson));
    }

}
like image 84
josev.junior Avatar answered Oct 30 '22 23:10

josev.junior


The only way to do this is to use a custom deserializer:

class CustomDeserializer extends JsonDeserializer<String> {

@Override
public String deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
    JsonNode node = jsonParser.readValueAsTree();
    if (node.asText().isEmpty()) {
        return null;
    }
    return node.toString();
}
}

Then do:

class EventBean {
public Long eventId;
public String title;

@JsonDeserialize(using = CustomDeserializer.class)
public String location;
}

This solution courtesy of Sach141 on this question.

like image 30
BlackHatSamurai Avatar answered Oct 30 '22 22:10

BlackHatSamurai


I had the same problem.

I hava a City class and sometimes I recive 'city':{} from a web service request.

So, the standard serializer create a new City with all empty field.

I created a custom deserializer in this way

public class CityJsonDeSerializer extends StdDeserializer<City> {

@Override
public City deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
    
    JsonNode node = jp.getCodec().readTree(jp);
    
    if(node.isNull() || node.asText().isEmpty()|| node.size()==0)
        return null;
    
    City city = new City();
    ... // set all fields
    return city;
}
}

The if check the conditions:

  • 'city' : null
  • 'city' : ''
  • 'city' : '{}'

and if it's true, the deserializer returns null.

like image 32
Daniele Licitra Avatar answered Oct 30 '22 21:10

Daniele Licitra