24

I have the following class, that is mapped by Jackson (simplified version):

public class POI {
    @JsonProperty("name")
    private String name;
}

In some cases the server returns "name": null and I would like to then set name to empty Java String.

Is there any Jackson annotation or should I just check for the null inside my getter and return empty string if the property is null?

1

5 Answers 5

29

Jackson 2.9 actually offers a new mechanism not yet mentioned: use of @JsonSetter for properties, and its equivalent "Config Overrides" for types like String.class. Longer explanation included in

https://medium.com/@cowtowncoder/jackson-2-9-features-b2a19029e9ff

but gist is that you can either mark field (or setter) like so:

@JsonSetter(nulls=Nulls.AS_EMPTY) public String stringValue;

or configure mapper to do the same for all String value properties:

mapper.configOverride(String.class)
 .setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));

both of which would convert incoming null into empty value, which for Strings is "".

This also works for Collections and Maps as expected.

Sign up to request clarification or add additional context in comments.

4 Comments

Setting @JsonSetter(nulls = Nulls.AS_EMPTY) worked for me when API could either send "null" as a value or not send the field at all. In both cases it was mapped to an empty string. Exactly what I needed, thanks!
For collections: this doesn't work for missing properties, at least with Jackson 2.11.3. What does work when deserializing both explicit null values and missing properties is a final collection field initialized to an empty collection, with a getter only, and specifying Nulls.SKIP - either on the field itself or by configuring the mapper as specified in this answer.
@YanivNahoum correct, properties without incoming values are not handled, with the exception of Constructor-passed (@JsonCreator) case in which some value must be provided . So you are correct in that this is one way to work around the limitation.
@AbstractVoid what version of Jackson are you using? it only works when an explicit null is sent. Not when the field is missing
17

Again, this answer is for the SO users who happen to stumble upon this thread.

While the accepted answer stands accepted and valid in all its sense - it did not help me in the case where the decision to set null string values to empty string came only after we made our services available to iOS clients.

So, around 30-40 pojo's(increasing) and initializing them while instantiating the object in question or at the point of declaration was too much.

Here's how we did it.

public class CustomSerializerProvider extends DefaultSerializerProvider {

    public CustomSerializerProvider() {
        super();
    }

    public CustomSerializerProvider(CustomSerializerProvider provider, SerializationConfig config,
            SerializerFactory jsf) {
        super(provider, config, jsf);
    }

    @Override
    public CustomSerializerProvider createInstance(SerializationConfig config, SerializerFactory jsf) {
        return new CustomSerializerProvider(this, config, jsf);
    }

    @Override
    public JsonSerializer<Object> findNullValueSerializer(BeanProperty property) throws JsonMappingException {
        if (property.getType().getRawClass().equals(String.class))
            return Serializers.EMPTY_STRING_SERIALIZER_INSTANCE;
        else
            return super.findNullValueSerializer(property);
    }
}

And, the serializer

public class Serializers extends JsonSerializer<Object> {
    public static final JsonSerializer<Object> EMPTY_STRING_SERIALIZER_INSTANCE = new EmptyStringSerializer();

    public Serializers() {}

    @Override
    public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
            throws IOException, JsonProcessingException {
        jsonGenerator.writeString("");
    }

    private static class EmptyStringSerializer extends JsonSerializer<Object> {
        public EmptyStringSerializer() {}

        @Override
        public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
                throws IOException, JsonProcessingException {
            jsonGenerator.writeString("");
        }
    }
}

And, then set the serializer in the ObjectMapper. (Jackson 2.7.4)

ObjectMapper nullMapper = new ObjectMapper();
nullMapper.setSerializerProvider(new CustomSerializerProvider());

Hoping, this will save someone some time.

3 Comments

SO users means what?
stackoverflow -> SO
i see, thk u answer
12

A simple solution using no Jackson specialities: Write a Getter for name which returns an empty String instead of null as Jackson uses those to serialize.

public String getName() {
  return name != null ? name : "";
}

Another way would be to write a custom deserializer. Look here: http://wiki.fasterxml.com/JacksonHowToCustomSerializers

Comments

9

You can either set it in the default constructor, or on declaration:

public class POI {
    @JsonProperty("name")
    private String name; 

    public POI() {
        name = "";
    }
}

OR

public class POI {
    @JsonProperty("name")
    private String name = "";
} 

1 Comment

Great answer. ¿Any solution when the no parameter constructor is managed by Lombok?
1

In case you are looking for a global solution for spring boot, you can configure the ObjectMapper

@Configuration
public class JacksonConfiguration {

    @Bean
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder mapperBuilder) {
        DefaultSerializerProvider sp = new DefaultSerializerProvider.Impl();
        sp.setNullValueSerializer(new JsonSerializer<Object>() {
            public void serialize(Object value, JsonGenerator jgen,
                                  SerializerProvider provider)
                    throws IOException, JsonProcessingException
            {
                jgen.writeString("");
            }
        });
        ObjectMapper mapper = mapperBuilder.build();
        mapper.setSerializerProvider(sp);
        return mapper;
    }

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.