OpenAPITools / jackson-databind-nullable

JsonNullable wrapper class and Jackson module to support meaningful null values
Apache License 2.0
103 stars 30 forks source link
jackson jackson-databind nullable optional

jackson-databind-nullable

Build Status

This module provides a JsonNullable wrapper class and a Jackson module to serialize/deserialize it. The JsonNullable wrapper shall be used to wrap Java bean fields for which it is important to distinguish between an explicit "null" and the field not being present. A typical usage is when implementing Json Merge Patch where an explicit "null"has the meaning "set this field to null / remove this field" whereas a non-present field has the meaning "don't change the value of this field".

The module comes with an integrated ValueExtractor that automatically unwraps the contained value of the JsonNullable if used together with javax.validation Bean validation (JSR 380).

Note : a lot of people use Optional to bring this behavior. Although it kinda works, it's not a good idea because:

Installation

The module is compatible with JDK8+

./mvnw clean install

Usage

JsonNullable shall primarily be used in bean fields.

If we have the following class

public static class Pet {

    @Size(max = 10)   
    public JsonNullable<String> name = JsonNullable.undefined();

    public Pet name(JsonNullable<String> name) {
        this.name = name;
        return this;
    }
}

And we instantiate the mapper either for JSON

import com.fasterxml.jackson.databind.ObjectMapper;

// ...

ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.registerModule(new JsonNullableModule());

or for XML

import com.fasterxml.jackson.dataformat.xml.XmlMapper;

// ...

XmlMapper xmlMapper = new XmlMapper();
xmlMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
xmlMapper.registerModule(new JsonNullableModule());

Then we can serialize

assertEquals("{}", mapper.writeValueAsString(new Pet().name(JsonNullable.<String>undefined())));
assertEquals("{\"name\":null}", mapper.writeValueAsString(new Pet().name(JsonNullable.<String>of(null))));
assertEquals("{\"name\":\"Rex\"}", mapper.writeValueAsString(new Pet().name(JsonNullable.of("Rex"))));

and deserialize

assertEquals(JsonNullable.of("Rex"), mapper.readValue("{\"name\":\"Rex\"}", Pet.class).name);
assertEquals(JsonNullable.<String>of(null), mapper.readValue("{\"name\":null}", Pet.class).name);
assertEquals(JsonNullable.<String>undefined(), mapper.readValue("{}", Pet.class).name);

The ValueExtractor is registered automatically via Java Service loader mechanism. The example class above will validate as follows

// instantiate javax.validation.Validator
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Pet myPet = new Pet().name(JsonNullable.of("My Pet's really long name"));
Set<ConstraintViolation<Pet>> validationResult = validator.validate(myPet);
assertTrue(1, validationResult.size());

Limitations