Skip to content
3 changes: 3 additions & 0 deletions release-notes/CREDITS-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -1863,6 +1863,9 @@ wrongwrong (@k163377)
* Contributed fix for #5139: In `CollectionDeserializer`, `JsonSetter.contentNulls`
is sometimes ignored
(2.19.1)
* Contributed fix for #5202: #5202: `JsonSetter.contentNulls` ignored for `Object[]`,
`String[]` and `Collection<String>`
(2.19.2)

Bernd Ahlers (@bernd)
* Reported #4742: Deserialization with Builder, External type id, `@JsonCreator` failing
Expand Down
3 changes: 3 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Project: jackson-databind

2.19.2 (not yet released)

#5202: `JsonSetter.contentNulls` ignored for `Object[]`, `String[]`
and `Collection<String>`
(fix by @wrongwrong)
#5215: `@JsonAnyGetter` serialization order change from 2.18.4 to 2.19.0
(reported by Eddú M)
(fix by Joo-Hyuk K)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,19 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt)
if (_skipNullValues) {
continue;
}
value = _nullProvider.getNullValue(ctxt);
value = null;
} else {
value = _deserializeNoNullChecks(p, ctxt);
}

if (value == null) {
value = _nullProvider.getNullValue(ctxt);

if (value == null && _skipNullValues) {
continue;
}
}

if (ix >= chunk.length) {
chunk = buffer.appendCompletedChunk(chunk);
ix = 0;
Expand Down Expand Up @@ -275,10 +284,19 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt,
if (_skipNullValues) {
continue;
}
value = _nullProvider.getNullValue(ctxt);
value = null;
} else {
value = _deserializeNoNullChecks(p, ctxt);
}

if (value == null) {
value = _nullProvider.getNullValue(ctxt);

if (value == null && _skipNullValues) {
continue;
}
}

if (ix >= chunk.length) {
chunk = buffer.appendCompletedChunk(chunk);
ix = 0;
Expand Down Expand Up @@ -346,7 +364,7 @@ protected Object handleNonArray(JsonParser p, DeserializationContext ctxt)
if (_skipNullValues) {
return _emptyValue;
}
value = _nullProvider.getNullValue(ctxt);
value = null;
} else {
if (p.hasToken(JsonToken.VALUE_STRING)) {
String textValue = p.getText();
Expand All @@ -371,6 +389,15 @@ protected Object handleNonArray(JsonParser p, DeserializationContext ctxt)

value = _deserializeNoNullChecks(p, ctxt);
}

if (value == null) {
value = _nullProvider.getNullValue(ctxt);

if (value == null && _skipNullValues) {
return _emptyValue;
}
}

// Ok: bit tricky, since we may want T[], not just Object[]
Object[] result;

Expand Down Expand Up @@ -399,4 +426,3 @@ protected Object _deserializeNoNullChecks(JsonParser p, DeserializationContext c
return _elementDeserializer.deserializeWithType(p, ctxt, _elementTypeDeserializer);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,17 @@ public String[] deserialize(JsonParser p, DeserializationContext ctxt) throws IO
if (_skipNullValues) {
continue;
}
value = (String) _nullProvider.getNullValue(ctxt);
} else {
value = _parseString(p, ctxt, _nullProvider);
}

if (value == null) {
value = (String) _nullProvider.getNullValue(ctxt);

if (value == null && _skipNullValues) {
continue;
}
}
}
if (ix >= chunk.length) {
chunk = buffer.appendCompletedChunk(chunk);
Expand Down Expand Up @@ -219,13 +226,22 @@ private String[] _deserializeCustom(JsonParser p, DeserializationContext ctxt,
if (_skipNullValues) {
continue;
}
value = (String) _nullProvider.getNullValue(ctxt);
value = null;
} else {
value = deser.deserialize(p, ctxt);
}
} else {
value = deser.deserialize(p, ctxt);
}

if (value == null) {
value = (String) _nullProvider.getNullValue(ctxt);

if (value == null && _skipNullValues) {
continue;
}
}

if (ix >= chunk.length) {
chunk = buffer.appendCompletedChunk(chunk);
ix = 0;
Expand Down Expand Up @@ -283,10 +299,17 @@ public String[] deserialize(JsonParser p, DeserializationContext ctxt,
if (_skipNullValues) {
return NO_STRINGS;
}
value = (String) _nullProvider.getNullValue(ctxt);
} else {
value = _parseString(p, ctxt, _nullProvider);
}

if (value == null) {
value = (String) _nullProvider.getNullValue(ctxt);

if (value == null && _skipNullValues) {
continue;
}
}
}
if (ix >= chunk.length) {
chunk = buffer.appendCompletedChunk(chunk);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,18 @@ public Collection<String> deserialize(JsonParser p, DeserializationContext ctxt,
if (_skipNullValues) {
continue;
}
value = (String) _nullProvider.getNullValue(ctxt);
} else {
value = _parseString(p, ctxt, _nullProvider);
}

if (value == null) {
value = (String) _nullProvider.getNullValue(ctxt);

if (value == null && _skipNullValues) {
continue;
}
}

result.add(value);
}
} catch (Exception e) {
Expand Down Expand Up @@ -246,13 +254,22 @@ private Collection<String> deserializeUsingCustom(JsonParser p, DeserializationC
if (_skipNullValues) {
continue;
}
value = (String) _nullProvider.getNullValue(ctxt);
value = null;
} else {
value = deser.deserialize(p, ctxt);
}
} else {
value = deser.deserialize(p, ctxt);
}

if (value == null) {
value = (String) _nullProvider.getNullValue(ctxt);

if (value == null && _skipNullValues) {
continue;
}
}

result.add(value);
}
} catch (Exception e) {
Expand Down Expand Up @@ -297,7 +314,7 @@ private final Collection<String> handleNonArray(JsonParser p, DeserializationCon
if (_skipNullValues) {
return result;
}
value = (String) _nullProvider.getNullValue(ctxt);
value = null;
} else {
if (p.hasToken(JsonToken.VALUE_STRING)) {
String textValue = p.getText();
Expand Down Expand Up @@ -326,6 +343,15 @@ private final Collection<String> handleNonArray(JsonParser p, DeserializationCon
throw JsonMappingException.wrapWithPath(e, result, result.size());
}
}

if (value == null) {
value = (String) _nullProvider.getNullValue(ctxt);

if (value == null && _skipNullValues) {
return result;
}
}

result.add(value);
return result;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.fasterxml.jackson.databind.deser.jdk;

import org.junit.jupiter.api.Test;

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

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.InvalidNullException;
import com.fasterxml.jackson.databind.json.JsonMapper;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

// For [databind#5165]
public class ObjectArrayDeserializer5165Test
{
static class Dst {
public Integer[] array;
}

@Test
public void nullsFailTest() {
ObjectMapper mapper = JsonMapper.builder()
.defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL))
.build();

// NOTE! Relies on default coercion of "" into `null` for `Integer`s...
assertThrows(
InvalidNullException.class,
() -> mapper.readValue("{\"array\":[\"\"]}", Dst.class)
);
}

@Test
public void nullsSkipTest() throws Exception {
ObjectMapper mapper = JsonMapper.builder()
.defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP))
.build();

Dst dst = mapper.readValue("{\"array\":[\"\"]}", Dst.class);
// NOTE! Relies on default coercion of "" into `null` for `Integer`s...
assertEquals(0, dst.array.length, "Null values should be skipped");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.fasterxml.jackson.databind.deser.jdk;

import java.io.IOException;

import org.junit.jupiter.api.Test;

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

import com.fasterxml.jackson.core.JsonParser;

import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.exc.InvalidNullException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

// For [databind#5165]
public class StringArrayDeserializer5165Test
{
static class Dst {
public String[] array;
}

// Custom deserializer that converts empty strings to null
static class EmptyStringToNullDeserializer extends StdDeserializer<String> {
private static final long serialVersionUID = 1L;

public EmptyStringToNullDeserializer() {
super(String.class);
}

@Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String value = p.getValueAsString();
if (value != null && value.isEmpty()) {
return null;
}
return value;
}
}

private ObjectMapper createMapperWithCustomDeserializer() {
SimpleModule module = new SimpleModule()
.addDeserializer(String.class, new EmptyStringToNullDeserializer());

return JsonMapper.builder()
.addModule(module)
.defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL))
.build();
}

@Test
public void nullsFailTest() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as with Object[], no nulls exists in test JSON; test does not trigger handling at all.

ObjectMapper mapper = createMapperWithCustomDeserializer();

assertThrows(
InvalidNullException.class,
() -> mapper.readValue("{\"array\":[\"\"]}", Dst.class)
);
}

@Test
public void nullsSkipTest() throws Exception {
SimpleModule module = new SimpleModule()
.addDeserializer(String.class, new EmptyStringToNullDeserializer());

ObjectMapper mapper = JsonMapper.builder()
.addModule(module)
.defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP))
.build();

Dst dst = mapper.readValue("{\"array\":[\"\"]}", Dst.class);

assertEquals(0, dst.array.length, "Null values should be skipped");
}
}
Loading