Skip to content

Commit 08e6e13

Browse files
authored
Fixed problem in CollectionDeserializer where _nullProvider was not used if it was not JsonToken.VALUE_NULL (#5140)
1 parent 4924007 commit 08e6e13

File tree

4 files changed

+109
-19
lines changed

4 files changed

+109
-19
lines changed

release-notes/CREDITS-2.x

+3
Original file line numberDiff line numberDiff line change
@@ -1860,6 +1860,9 @@ wrongwrong (@k163377)
18601860
`@JsonDeserialize(keyUsing = ...)` is overwritten by the `KeyDeserializer`
18611861
specified in the `ObjectMapper`.
18621862
(2.18.3)
1863+
* Contributed fix for #5139: In `CollectionDeserializer`, `JsonSetter.contentNulls`
1864+
is sometimes ignored
1865+
(2.19.1)
18631866

18641867
Bernd Ahlers (@bernd)
18651868
* Reported #4742: Deserialization with Builder, External type id, `@JsonCreator` failing

release-notes/VERSION-2.x

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ Project: jackson-databind
44
=== Releases ===
55
------------------------------------------------------------------------
66

7+
2.19.1 (not yet released)
8+
9+
#5139: In `CollectionDeserializer`, `JsonSetter.contentNulls` is sometimes ignored
10+
(contributed by @wrongwrong)
11+
712
2.19.0 (24-Apr-2025)
813

914
#1467: Support `@JsonUnwrapped` with `@JsonCreator`

src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java

+47-19
Original file line numberDiff line numberDiff line change
@@ -353,16 +353,21 @@ protected Collection<Object> _deserializeFromArray(JsonParser p, Deserialization
353353
if (_skipNullValues) {
354354
continue;
355355
}
356-
value = _nullProvider.getNullValue(ctxt);
357-
} else if (_valueTypeDeserializer == null) {
358-
value = _valueDeserializer.deserialize(p, ctxt);
356+
value = null;
359357
} else {
360-
value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
358+
value = _deserializeNoNullChecks(p, ctxt);
361359
}
360+
362361
if (value == null) {
363-
_tryToAddNull(p, ctxt, result);
364-
continue;
362+
value = _nullProvider.getNullValue(ctxt);
363+
364+
// _skipNullValues is checked by _tryToAddNull.
365+
if (value == null) {
366+
_tryToAddNull(p, ctxt, result);
367+
continue;
368+
}
365369
}
370+
366371
result.add(value);
367372

368373
/* 17-Dec-2017, tatu: should not occur at this level...
@@ -398,6 +403,7 @@ protected final Collection<Object> handleNonArray(JsonParser p, DeserializationC
398403
if (!canWrap) {
399404
return (Collection<Object>) ctxt.handleUnexpectedToken(_containerType, p);
400405
}
406+
401407
Object value;
402408

403409
try {
@@ -406,16 +412,19 @@ protected final Collection<Object> handleNonArray(JsonParser p, DeserializationC
406412
if (_skipNullValues) {
407413
return result;
408414
}
409-
value = _nullProvider.getNullValue(ctxt);
410-
} else if (_valueTypeDeserializer == null) {
411-
value = _valueDeserializer.deserialize(p, ctxt);
415+
value = null;
412416
} else {
413-
value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
417+
value = _deserializeNoNullChecks(p, ctxt);
414418
}
415-
// _skipNullValues is checked by _tryToAddNull.
419+
416420
if (value == null) {
417-
_tryToAddNull(p, ctxt, result);
418-
return result;
421+
value = _nullProvider.getNullValue(ctxt);
422+
423+
// _skipNullValues is checked by _tryToAddNull.
424+
if (value == null) {
425+
_tryToAddNull(p, ctxt, result);
426+
return result;
427+
}
419428
}
420429
} catch (Exception e) {
421430
boolean wrap = ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
@@ -447,18 +456,21 @@ protected Collection<Object> _deserializeWithObjectId(JsonParser p, Deserializat
447456
while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
448457
try {
449458
Object value;
459+
450460
if (t == JsonToken.VALUE_NULL) {
451461
if (_skipNullValues) {
452462
continue;
453463
}
454-
value = _nullProvider.getNullValue(ctxt);
455-
} else if (_valueTypeDeserializer == null) {
456-
value = _valueDeserializer.deserialize(p, ctxt);
464+
value = null;
457465
} else {
458-
value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
466+
value = _deserializeNoNullChecks(p, ctxt);
459467
}
460-
if (value == null && _skipNullValues) {
461-
continue;
468+
469+
if (value == null) {
470+
value = _nullProvider.getNullValue(ctxt);
471+
if (value == null && _skipNullValues) {
472+
continue;
473+
}
462474
}
463475
referringAccumulator.add(value);
464476
} catch (UnresolvedForwardReference reference) {
@@ -475,6 +487,22 @@ protected Collection<Object> _deserializeWithObjectId(JsonParser p, Deserializat
475487
return result;
476488
}
477489

490+
/**
491+
* Deserialize the content of the collection.
492+
* If _valueTypeDeserializer is null, use _valueDeserializer.deserialize; if non-null,
493+
* use _valueDeserializer.deserializeWithType to deserialize value.
494+
* This method only performs deserialization and does not consider _skipNullValues, _nullProvider, etc.
495+
* @since 2.19.1
496+
*/
497+
protected Object _deserializeNoNullChecks(JsonParser p,DeserializationContext ctxt)
498+
throws IOException
499+
{
500+
if (_valueTypeDeserializer == null) {
501+
return _valueDeserializer.deserialize(p, ctxt);
502+
}
503+
return _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
504+
}
505+
478506
/**
479507
* {@code java.util.TreeSet} (and possibly other {@link Collection} types) does not
480508
* allow addition of {@code null} values, so isolate handling here.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.fasterxml.jackson.databind.deser.jdk;
2+
3+
import java.util.List;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
import com.fasterxml.jackson.annotation.JsonSetter;
8+
import com.fasterxml.jackson.annotation.Nulls;
9+
import com.fasterxml.jackson.core.type.TypeReference;
10+
import com.fasterxml.jackson.databind.ObjectMapper;
11+
import com.fasterxml.jackson.databind.exc.InvalidNullException;
12+
import com.fasterxml.jackson.databind.json.JsonMapper;
13+
14+
import static org.junit.jupiter.api.Assertions.assertThrows;
15+
import static org.junit.jupiter.api.Assertions.assertTrue;
16+
17+
// For [databind#5139]
18+
public class CollectionDeserializer5139Test
19+
{
20+
static class Dst {
21+
private List<Integer> list;
22+
23+
public List<Integer> getList() {
24+
return list;
25+
}
26+
27+
public void setList(List<Integer> list) {
28+
this.list = list;
29+
}
30+
}
31+
32+
@Test
33+
public void nullsFailTest() {
34+
ObjectMapper mapper = JsonMapper.builder()
35+
.defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.FAIL))
36+
.build();
37+
38+
assertThrows(
39+
InvalidNullException.class,
40+
() -> mapper.readValue("{\"list\":[\"\"]}", new TypeReference<Dst>(){})
41+
);
42+
}
43+
44+
@Test
45+
public void nullsSkipTest() throws Exception {
46+
ObjectMapper mapper = JsonMapper.builder()
47+
.defaultSetterInfo(JsonSetter.Value.forContentNulls(Nulls.SKIP))
48+
.build();
49+
50+
Dst dst = mapper.readValue("{\"list\":[\"\"]}", new TypeReference<Dst>() {});
51+
52+
assertTrue(dst.getList().isEmpty());
53+
}
54+
}

0 commit comments

Comments
 (0)