Given the following SampleTestClass where I try to parse JSON to Java:
class EmptyListDeserializerTest {
final ObjectMapper objectMapper = new ObjectMapper();
{
objectMapper.configOverride(List.class)
.setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY, Nulls.AS_EMPTY));
}
@Test
void test2() throws JsonProcessingException {
final var result = objectMapper.readValue("null", new TypeReference<List<MyClass>>() {});
assertThat(result).isEmpty();
}
@Test
void test3() throws JsonProcessingException {
final var json = """
[{
"name": "John Doe",
"address": null
}]
""";
final var result = objectMapper.readValue(json, new TypeReference<List<MyComplexClass>>() {});
assertThat(result.get(0).address).isEmpty();
}
@Test
void test4() throws JsonProcessingException {
final var json = """
[{
"name": "John Doe"
}]
""";
final var result = objectMapper.readValue(json, new TypeReference<List<MyComplexClass>>() {});
assertThat(result.get(0).address).isEmpty();
}
public static class MyClass {
public MyClass() {
}
private String name;
public MyClass(final String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
}
public static class MyComplexClass {
public MyComplexClass() {
}
private String name;
private List<String> address;
public MyComplexClass(final String name, final List<String> address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public List<String> getAddress() {
return address;
}
public void setAddress(final List<String> address) {
this.address = address;
}
}
}
I want to achieve to map all kind of non-existing lists to empty lists.
Adapting the entities itself is not a solution. Only the ObjectMapper is adaptable.
I tried all kind of potential solutions , but only managed to get test3 to pass, all others fail.
I also tried a CustomerDeserializer, but it was never called, even though registered:
public List<?> deserialize(JsonParser p, DeserializationContext ctxt)
So, what am I missing?
--- EDIT ---
Using this config instead:
{
objectMapper.configOverride(List.class)
.setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY, Nulls.AS_EMPTY));
SimpleModule module = new SimpleModule();
module.addDeserializer(List.class, new EmptyListDeserializer());
this.objectMapper.registerModule(module);
}
and modifying the test2 slightly (adding escaped double-quotes):
@Test
void test2() throws JsonProcessingException {
final var result = objectMapper.readValue("\"null\"", new TypeReference<List<MyClass>>() {});
assertThat(result).isEmpty();
}
the test2 passes, but then test3 fails.
The CustomDeserializer looks like this:
public class EmptyListDeserializer extends JsonDeserializer<List<?>> {
@Override
public List<?> deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// Check for null or 'null' string value
if (p.getCurrentToken() == JsonToken.VALUE_NULL
|| p.getCurrentToken().isScalarValue()
|| (p.getCurrentToken() == JsonToken.VALUE_STRING && "null".equals(p.getText()))) {
return Collections.emptyList();
}
// Otherwise, proceed with default deserialization
return p.readValueAs(List.class);
}