5 votes

Activer la désérialisation Jackson des objets vides à Null

On m'a demandé de modifier notre configuration de mappage jackson afin que chaque objet vide que nous désérialisons (à partir de JSON) soit désérialisé en tant que null.

Le problème est que je m'efforce de le faire, mais sans succès. Voici un exemple de notre ObjectMapper configuration (et exemple) :

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;

J'ai pensé à quelque chose comme ajouter :

mapper.configure(
    DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);

mais ça ne fonctionne que sur les cordes.

J'ai peur que l'utilisation d'un désérialiseur personnalisé ne m'aide pas, car j'écris un mappeur générique (pour tous les objets). J'ai donc probablement besoin de quelque chose comme un délégant ou une méthode de désérialisation post-processus.

Donc pour json comme "" o {} Je m'attends à être converti en null en java (et non pas à une chaîne vide ou à une Object instance).

2voto

josev.junior Points 329

Qu'est-ce qu'un objet vide pour vous ? Un objet avec des champs de valeur nulle ? Un objet sans champs ? Vous pouvez créer un custom pour vérifier les noeuds et désérialiser comme vous voulez. Je ne vois aucun problème à l'utiliser de manière générique.

J'ai fait un petit exemple :

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

}

1voto

Daniele Licitra Points 464

J'ai eu le même problème.

J'ai un City et parfois je reçois 'city':{} à partir d'une demande de service web.

Ainsi, le sérialiseur standard crée une nouvelle ville avec tous les champs vides.

J'ai créé un désérialiseur personnalisé de cette façon

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

Le if vérifie les conditions :

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

et si c'est vrai, le désérialiseur retourne null .

0voto

BlackHatSamurai Points 6211

La seule façon de le faire est d'utiliser un désérialiseur personnalisé :

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

Alors, faites-le :

class EventBean {
public Long eventId;
public String title;

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

Cette solution est une courtoisie de Sach141 sur cette page question .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X