Enable Jackson Deserialization of Empty Objects to Null

非 Y 不嫁゛ 提交于 2020-08-03 02:20:12

问题


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


回答1:


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));
    }

}



回答2:


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.




回答3:


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.



来源:https://stackoverflow.com/questions/57464489/enable-jackson-deserialization-of-empty-objects-to-null

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!