2

I am calling an API that returns an array of values that are converted to a List<SomeEnum>. Some of the values in the response are no longer defined in the enum. These values appear in the list as null values.

I am wondering if there is a way to tell Jackson (or feign) to exclude null list items when deserializing.

I've looked at @JsonInclude but this applies to null properties and not null collection items.

EDIT

The reason the values show up as null is because the option DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL is being used.

Richard Collette
  • 5,462
  • 4
  • 53
  • 79

2 Answers2

3

You can use JsonSetter annotation together with Nulls.SKIP. See below example:

import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.Arrays;
import java.util.List;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();

        Work work = new Work();
        work.setDays(Arrays.asList(null, Day.Monday, Day.Tuesday, null, Day.Friday, null));

        String json = mapper.writeValueAsString(work);
        System.out.println(json);
        System.out.println(mapper.readValue(json, Work.class));
    }
}

enum Day {Monday, Tuesday, Wednesday, Thursday, Friday}

class Work {

    private List<Day> days;

    public List<Day> getDays() {
        return days;
    }

    @JsonSetter(contentNulls = Nulls.SKIP)
    public void setDays(List<Day> days) {
        this.days = days;
    }

    @Override
    public String toString() {
        return "Created{" +
                "days=" + days +
                '}';
    }
}

Above code prints:

{"days":[null,"Monday","Tuesday",null,"Friday",null]}
Created{days=[Monday, Tuesday, Friday]}

EDIT
Above solution will not work, if you have unknown enum values. In that case you should use DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL or DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE feature. One workaround would be filtering collection from null-s manually:

public void setDays(List<Day> days) {
    this.days = days.stream().filter(Objects::nonNull).collect(Collectors.toList());
}

Of course, we could implement custom deserialiser for collection and skip it there but...

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
  • Looks promising! – Richard Collette Jul 23 '19 at 15:05
  • That did not work. Using Jackson 2.99 (core and annotations). I wish I could paste a screen shot with the debugger and code but in the setter method body, the list contains null elements. The difference here may be that you are deserializing `null` values whereas in my case, I am deserializing `enum` values that are not defined in the `enum`. The documentation for `contentNulls` talks about deserializing explicit `nulls`. – Richard Collette Jul 23 '19 at 16:22
  • @RichardCollette, when you wrote "These values appear in the list as null values." I understood that `null` appears in `JSON` and we need to skip `null`-s only. Please, take a look on this question: [How to ignore enum fields in Jackson JSON-to-Object mapping?](https://stackoverflow.com/questions/16446619/how-to-ignore-enum-fields-in-jackson-json-to-object-mapping). I will try to see what other solutions could work here. – Michał Ziober Jul 23 '19 at 18:42
  • @RichardCollette, also I am surprised you received `null` value for unknown instead of `com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type Day from String "Wednesday"` exception. – Michał Ziober Jul 23 '19 at 18:47
  • 1
    @RichardCollette, Probably you use `DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL` don't you? Have you tried to use `DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE` feature? You can create new default enum value and use it: `@JsonEnumDefaultValue DEFAULT`. – Michał Ziober Jul 23 '19 at 18:56
  • The issue here is that the control mechanisms mostly have to do with how a single enum is serialized. Setting it to null or a default value doesn't address the issue of how an enum is deserialized into a List. contentNulls is close but doesn't address unknown enum values as opposed to explicit nulls. I have a feeling, for now, that I am limited to removing the null values after deserialization. – Richard Collette Jul 24 '19 at 16:35
  • @RichardCollette, it looks like converting to `null` and skiping at the same type is out of the implementation now. Probably, we are forced to use some dirty workarounds or we can alway write custom collection deserialiser for our enum. – Michał Ziober Jul 24 '19 at 20:57
  • do we have something equivalent for getters? – Viny Machado Jul 22 '23 at 00:11
  • 1
    @VinyMachado, I do not think we have something similar for getters. In getters you can just filter out nulls using `Java Stream API`. – Michał Ziober Jul 22 '23 at 19:39
-1

Add the annotation @JsonInclude(Include.NON_NULL) to the class you wish to map. That will solve the issue

morrisng
  • 127
  • 7