2

Given the following class:

@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Account {

    [... A lot of serialized properties]

    @JsonSerialize(nullsUsing = JacksonSpringSpelSerializer.class, using = JacksonSpringSpelSerializer.class)
    @JsonView(View.Contract.class)
    @Value("#{@contractService.getActiveContract(#this)}")
    public Contract activeContract;

}

Basically the property activeContract is null, and its value is evaluated only when the correct @JsonView is provided, the value is computed by a Spring Spel expression, everything is done in a custom Serializer JacksonSpringSpelSerializer.

Everything works as expected BUT the computed value can sometimes be null, which is normal, and I end up with a json like this:

{
    [... All properties],
    "activeContract": null
}

The issue is that I don't want null properties to be in the returned json, the @JsonInclude(JsonInclude.Include.NON_EMPTY) is ignored when a custom serializer is set on a property.

After digging a bit, I found out that the custom serializer is called by BeanPropertyWriter.serializeAsField() that contains:

    if (value == null) {
        if (_nullSerializer != null) {
            gen.writeFieldName(_name);
            _nullSerializer.serialize(null, gen, prov);
        }
        return;
    }

So the name of the field is written by gen.writeFieldName(_name); before the custom serializer is actually called, and I didn't find a proper way to prevent this behavior or to remove the null properties generated by the custom Serializer.

Is there a proper way to achieve such a result ? Any advice would be very welcome :D

Thanks <3

anthofo
  • 93
  • 12
  • If it's a customer Jackson serializer couldn't you just add the logic of skipping null fields in the serialize() method of your custom serializer? – Jonck van der Kogel Feb 28 '20 at 10:29
  • That's what I did, but the serializer is called by a class that first writes the name of the property. If my serializer do nothing I get `{ "activeContract" }`. If the serializer performs a `gen.writeNull();`I get `{ "activeContract": null }`. I didn't find a way to skip the field from the serializer itself – anthofo Feb 28 '20 at 10:57
  • When you want to use `JsonInclude.Include.NON_EMPTY` together with custom serialiser, you need to implement `isEmpty` method as well. Take a look on similar example: [How can I serialize double value null and not return when 0.0](https://stackoverflow.com/questions/59573074/use-jackson-serialize-how-can-i-serialize-double-value-null-and-not-return-when) – Michał Ziober Feb 29 '20 at 17:08
  • I tried but unfortunately it didn't work, for the same issue, the value of the field is null and is possibily filled by the serializer. The Jackson `BeanPropertyWriter` doesn't check the isEmpty method if the annoted field is already null: https://github.com/FasterXML/jackson-databind/blob/74df6467a73723c18f4a45b363a192413c6d84d7/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java#L629 It worked for the double case because the serialized property was not null but probably < 0 – anthofo Mar 02 '20 at 10:11

3 Answers3

0

you can try use JsonInclude.Include.NON_NULL, like the follow code

@JsonInclude(JsonInclude.Include.NON_NULL)
  • That's what I tried, but looks like it doesn't work if a field has a custom Serialiser and the `JsonInclude` is just ignored – anthofo Feb 28 '20 at 10:59
  • @JsonSerialize(using = JacksonSpringSpelSerializer.class) ; remove 'nullsUsing = JacksonSpringSpelSerializer.class', It might work correctly – ZhongjinHacker Feb 28 '20 at 11:34
  • I can't, because the value is null, and is filled by the serializer ^^ – anthofo Feb 28 '20 at 13:01
0

If you already specified Include.NON_EMPTY in some way either globally or on the field, you have to also override com.fasterxml.jackson.databind.JsonSerializer#isEmpty(com.fasterxml.jackson.databind.SerializerProvider, T)

static class Serializer extends JsonSerializer<Foo> {

    @Override
    public void serialize(Foo value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        // serialize
    }

    @Override
    public boolean isEmpty(SerializerProvider provider, Foo value) {
        // this will be called for Include.NON_EMPTY
        boolean isItEmpty = // get it somehow
        return isItEmpty;
    }
}
Pawel Zieminski
  • 439
  • 3
  • 8
0

This can be fixed by adding the include attribute in the annotation:

 @JsonSerialize(using=JacksonSpringSpelSerializer.class, include=JsonInclude.NON_NULL)

The field level default for this in JsonSerialize is ALWAYS, which overrides the class level setting, hence the undesired behavior.

M. Jessup
  • 8,153
  • 1
  • 29
  • 29