Skip to content

Commit 3df1268

Browse files
committed
Merge remote-tracking branch 'upstream/2.16' into 3877-requireTypeIdForSubtype
2 parents ba36af7 + 23603ea commit 3df1268

File tree

15 files changed

+504
-21
lines changed

15 files changed

+504
-21
lines changed

release-notes/CREDITS-2.x

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1583,16 +1583,18 @@ Hervé Boutemy (hboutemy@github)
15831583
(2.15.0)
15841584
15851585
Sim Yih Tsern (yihtsern@github)
1586-
* Contributed fix for #2974: Null coercion with `@JsonSetter` does not work with `java.lang.Record`
1586+
* Contributed fix for #2974: Null coercion with `@JsonSetter` does not work with `java.lang.Record`
15871587
(2.15.0)
1588-
* Contributed fix for #2992: Properties naming strategy do not work with Record
1588+
* Contributed fix for #2992: Properties naming strategy do not work with Record
15891589
(2.15.0)
1590-
* Contributed fix for #3180: Support `@JsonCreator` annotation on record classes
1590+
* Contributed fix for #3180: Support `@JsonCreator` annotation on record classes
15911591
(2.15.0)
1592-
* Contributed fix for #3297: `@JsonDeserialize(converter = ...)` does not work with Records
1592+
* Contributed fix for #3297: `@JsonDeserialize(converter = ...)` does not work with Records
15931593
(2.15.0)
1594-
* Contributed fix for #3342: `JsonTypeInfo.As.EXTERNAL_PROPERTY` does not work with record wrappers
1594+
* Contributed fix for #3342: `JsonTypeInfo.As.EXTERNAL_PROPERTY` does not work with record wrappers
15951595
(2.15.0)
1596+
* Contributed fix for #3894: Only avoid Records fields detection for deserialization
1597+
(2.15.1)
15961598
15971599
Ajay Siwach (Siwach16@github)
15981600
* Contributed #3637: Add enum features into `@JsonFormat.Feature`

release-notes/VERSION-2.x

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ Project: jackson-databind
88

99
No changes since 2.15
1010

11+
2.15.1 (not yet released)
12+
13+
#3894: Only avoid Records fields detection for deserialization
14+
(contributed by Sim Y-T)
15+
#3913: Issue with deserialization when there are unexpected properties (due
16+
to null `StreamReadConstraints`)
17+
(reported by @sbertault)
18+
#3914: Fix TypeId serialization for `JsonTypeInfo.Id.DEDUCTION`, native type ids
19+
1120
2.15.0 (23-Apr-2023)
1221

1322
#2536: Add `EnumFeature.READ_ENUM_KEYS_USING_INDEX` to work with

src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, final Deseri
454454

455455
// polymorphic?
456456
if (bean.getClass() != _beanType.getRawClass()) {
457-
return handlePolymorphic(p, ctxt, bean, unknown);
457+
return handlePolymorphic(p, ctxt, p.streamReadConstraints(), bean, unknown);
458458
}
459459
if (unknown != null) { // nope, just extra unknown stuff...
460460
bean = handleUnknownProperties(ctxt, bean, unknown);
@@ -538,7 +538,7 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, final Deseri
538538
if (unknown != null) {
539539
// polymorphic?
540540
if (bean.getClass() != _beanType.getRawClass()) { // lgtm [java/dereferenced-value-may-be-null]
541-
return handlePolymorphic(null, ctxt, bean, unknown);
541+
return handlePolymorphic(null, ctxt, p.streamReadConstraints(), bean, unknown);
542542
}
543543
// no, just some extra unknown properties
544544
return handleUnknownProperties(ctxt, bean, unknown);

src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1735,18 +1735,41 @@ protected void handleIgnoredProperty(JsonParser p, DeserializationContext ctxt,
17351735
* @param p (optional) If not null, parser that has more properties to handle
17361736
* (in addition to buffered properties); if null, all properties are passed
17371737
* in buffer
1738+
* @deprecated use {@link #handlePolymorphic(JsonParser, DeserializationContext, StreamReadConstraints, Object, TokenBuffer)}
17381739
*/
1740+
@Deprecated
17391741
protected Object handlePolymorphic(JsonParser p, DeserializationContext ctxt,
17401742
Object bean, TokenBuffer unknownTokens)
17411743
throws IOException
1744+
{
1745+
final StreamReadConstraints streamReadConstraints = p == null ?
1746+
StreamReadConstraints.defaults() : p.streamReadConstraints();
1747+
return handlePolymorphic(p, ctxt, streamReadConstraints, bean, unknownTokens);
1748+
}
1749+
1750+
/**
1751+
* Method called in cases where we may have polymorphic deserialization
1752+
* case: that is, type of Creator-constructed bean is not the type
1753+
* of deserializer itself. It should be a sub-class or implementation
1754+
* class; either way, we may have more specific deserializer to use
1755+
* for handling it.
1756+
*
1757+
* @param p (optional) If not null, parser that has more properties to handle
1758+
* (in addition to buffered properties); if null, all properties are passed
1759+
* in buffer
1760+
* @since 2.15.1
1761+
*/
1762+
protected Object handlePolymorphic(JsonParser p, DeserializationContext ctxt,
1763+
StreamReadConstraints streamReadConstraints, Object bean, TokenBuffer unknownTokens)
1764+
throws IOException
17421765
{
17431766
// First things first: maybe there is a more specific deserializer available?
17441767
JsonDeserializer<Object> subDeser = _findSubclassDeserializer(ctxt, bean, unknownTokens);
17451768
if (subDeser != null) {
17461769
if (unknownTokens != null) {
17471770
// need to add END_OBJECT marker first
17481771
unknownTokens.writeEndObject();
1749-
JsonParser p2 = unknownTokens.asParser(p.streamReadConstraints());
1772+
JsonParser p2 = unknownTokens.asParser(streamReadConstraints);
17501773
p2.nextToken(); // to get to first data field
17511774
bean = subDeser.deserialize(p2, ctxt, bean);
17521775
}

src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p,
395395
}
396396
// polymorphic?
397397
if (builder.getClass() != _beanType.getRawClass()) {
398-
return handlePolymorphic(p, ctxt, builder, unknown);
398+
return handlePolymorphic(p, ctxt, p.streamReadConstraints(), builder, unknown);
399399
}
400400
if (unknown != null) { // nope, just extra unknown stuff...
401401
builder = handleUnknownProperties(ctxt, builder, unknown);
@@ -440,7 +440,7 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p,
440440
if (unknown != null) {
441441
// polymorphic?
442442
if (builder.getClass() != _beanType.getRawClass()) {
443-
return handlePolymorphic(null, ctxt, builder, unknown);
443+
return handlePolymorphic(null, ctxt, p.streamReadConstraints(), builder, unknown);
444444
}
445445
// no, just some extra unknown properties
446446
return handleUnknownProperties(ctxt, builder, unknown);
@@ -669,7 +669,7 @@ protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser p,
669669
continue; // never gets here
670670
}
671671
if (builder.getClass() != _beanType.getRawClass()) {
672-
return handlePolymorphic(p, ctxt, builder, tokens);
672+
return handlePolymorphic(p, ctxt, p.streamReadConstraints(), builder, tokens);
673673
}
674674
return deserializeWithUnwrapped(p, ctxt, builder, tokens);
675675
}

src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,8 @@ protected void collectAll()
440440

441441
// 15-Jan-2023, tatu: [databind#3736] Let's avoid detecting fields of Records
442442
// altogether (unless we find a good reason to detect them)
443-
if (!isRecordType()) {
443+
// 17-Apr-2023: Need Records' fields for serialization for cases like [databind#3895] & [databind#3628]
444+
if (!isRecordType() || _forSerialization) {
444445
_addFields(props); // note: populates _fieldRenameMappings
445446
}
446447
_addMethods(props);

src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsDeductionTypeSerializer.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
66
import com.fasterxml.jackson.core.JsonGenerator;
7+
import com.fasterxml.jackson.core.JsonToken;
78
import com.fasterxml.jackson.core.type.WritableTypeId;
89
import com.fasterxml.jackson.databind.BeanProperty;
910

@@ -39,9 +40,19 @@ public WritableTypeId writeTypePrefix(JsonGenerator g,
3940
// write surrounding Object or Array start/end markers. But
4041
// we are not to generate type id to write (compared to base class)
4142

42-
if (idMetadata.valueShape.isStructStart()
43-
// also: do not try to write native type id
44-
&& !g.canWriteTypeId()) {
43+
if (idMetadata.valueShape.isStructStart()) {
44+
// 03-May-2023, tatu: [databind#3914]: should not write Native Type Id;
45+
// but may need to write the value start marker
46+
if (g.canWriteTypeId()) {
47+
idMetadata.wrapperWritten = false;
48+
if (idMetadata.valueShape == JsonToken.START_OBJECT) {
49+
g.writeStartObject(idMetadata.forValue);
50+
} else if (idMetadata.valueShape == JsonToken.START_ARRAY) {
51+
g.writeStartArray(idMetadata.forValue);
52+
}
53+
return idMetadata;
54+
}
55+
// But for non-wrapper types can just use the default handling
4556
return g.writeTypePrefix(idMetadata);
4657
}
4758
return null;

src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@
2121
* as well as some other commonly
2222
* needed aspects (addition of custom {@link AbstractTypeResolver}s,
2323
* {@link com.fasterxml.jackson.databind.deser.ValueInstantiator}s).
24+
* <p>
25+
* NOTE: that [de]serializers are registered as "default" [de]serializers.
26+
* As a result, they will have lower priority than the ones indicated through annotations on
27+
* both Class and property-associated annotations -- for example,
28+
* {@link com.fasterxml.jackson.databind.annotation.JsonDeserialize}.<br/>
29+
* In cases where both module-based [de]serializers and annotation-based [de]serializers are registered,
30+
* the [de]serializer specified by the annotation will take precedence.
2431
*<p>
2532
* NOTE: although it is not expected that sub-types should need to
2633
* override {@link #setupModule(SetupContext)} method, if they choose
@@ -312,6 +319,8 @@ protected SimpleModule setNamingStrategy(PropertyNamingStrategy naming) {
312319
*<p>
313320
* WARNING! "Last one wins" rule is applied.
314321
* Possible earlier addition of a serializer for a given Class will be replaced.
322+
* <p>
323+
* NOTE: This method registers "default" (de)serializers only. See a note on precedence in class JavaDoc.
315324
*/
316325
public SimpleModule addSerializer(JsonSerializer<?> ser)
317326
{
@@ -325,13 +334,17 @@ public SimpleModule addSerializer(JsonSerializer<?> ser)
325334

326335
/**
327336
* Method for adding serializer to handle values of specific type.
337+
* <p>
338+
* NOTE: This method registers "default" (de)serializers only. See a note on precedence in class JavaDoc.
328339
*<p>
329340
* WARNING! Type matching only uses type-erased {@code Class} and should NOT
330341
* be used when registering serializers for generic types like
331342
* {@link java.util.Collection} and {@link java.util.Map}.
332343
*<p>
333344
* WARNING! "Last one wins" rule is applied.
334345
* Possible earlier addition of a serializer for a given Class will be replaced.
346+
* <p>
347+
* NOTE: This method registers "default" (de)serializers only. See a note on precedence in class JavaDoc.
335348
*/
336349
public <T> SimpleModule addSerializer(Class<? extends T> type, JsonSerializer<T> ser)
337350
{
@@ -344,6 +357,9 @@ public <T> SimpleModule addSerializer(Class<? extends T> type, JsonSerializer<T>
344357
return this;
345358
}
346359

360+
/**
361+
* NOTE: This method registers "default" (de)serializers only. See a note on precedence in class JavaDoc.
362+
*/
347363
public <T> SimpleModule addKeySerializer(Class<? extends T> type, JsonSerializer<T> ser)
348364
{
349365
_checkNotNull(type, "type to register key serializer for");
@@ -370,6 +386,8 @@ public <T> SimpleModule addKeySerializer(Class<? extends T> type, JsonSerializer
370386
*<p>
371387
* WARNING! "Last one wins" rule is applied.
372388
* Possible earlier addition of a serializer for a given Class will be replaced.
389+
* <p>
390+
* NOTE: This method registers "default" (de)serializers only. See a note on precedence in class JavaDoc.
373391
*/
374392
public <T> SimpleModule addDeserializer(Class<T> type, JsonDeserializer<? extends T> deser)
375393
{
@@ -382,6 +400,9 @@ public <T> SimpleModule addDeserializer(Class<T> type, JsonDeserializer<? extend
382400
return this;
383401
}
384402

403+
/**
404+
* NOTE: This method registers "default" (de)serializers only. See a note on precedence in class JavaDoc.
405+
*/
385406
public SimpleModule addKeyDeserializer(Class<?> type, KeyDeserializer deser)
386407
{
387408
_checkNotNull(type, "type to register key deserializer for");
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.fasterxml.jackson.databind.failing;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.databind.BaseMapTest;
5+
6+
// [databinding#3897] This is failing test for `Record` class deserialization with single field annotated with
7+
// `JsonProperty.Access.WRITE_ONLY`. Regression from Jackson 2.14.2
8+
public class RecordDeserialization3897Test extends BaseMapTest {
9+
10+
record Example(
11+
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
12+
String value
13+
) {}
14+
15+
// Passes in 2.14.2, but does not in 2.15.0
16+
public void testRecordWithWriteOnly() throws Exception {
17+
final String JSON = a2q("{'value':'foo'}");
18+
19+
Example result = newJsonMapper().readValue(JSON, Example.class);
20+
21+
assertEquals("foo", result.value());
22+
}
23+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.fasterxml.jackson.databind.records;
2+
3+
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
4+
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
5+
import com.fasterxml.jackson.annotation.PropertyAccessor;
6+
import com.fasterxml.jackson.databind.BaseMapTest;
7+
import com.fasterxml.jackson.databind.ObjectMapper;
8+
9+
public class RecordIgnoreNonAccessorGetterTest extends BaseMapTest {
10+
11+
// [databind#3628]
12+
interface InterfaceWithGetter {
13+
14+
String getId();
15+
16+
String getName();
17+
}
18+
19+
@JsonPropertyOrder({"id", "name", "count"}) // easier to assert when JSON field ordering is always the same
20+
record RecordWithInterfaceWithGetter(String name) implements InterfaceWithGetter {
21+
22+
@Override
23+
public String getId() {
24+
return "ID:" + name;
25+
}
26+
27+
@Override
28+
public String getName() {
29+
return name;
30+
}
31+
32+
// [databind#3895]
33+
public int getCount() {
34+
return 999;
35+
}
36+
}
37+
38+
private final ObjectMapper MAPPER = newJsonMapper();
39+
40+
public void testSerializeIgnoreInterfaceGetter_WithoutUsingVisibilityConfig() throws Exception {
41+
String json = MAPPER.writeValueAsString(new RecordWithInterfaceWithGetter("Bob"));
42+
43+
assertEquals("{\"id\":\"ID:Bob\",\"name\":\"Bob\",\"count\":999}", json);
44+
}
45+
46+
public void testSerializeIgnoreInterfaceGetter_UsingVisibilityConfig() throws Exception {
47+
MAPPER.setVisibility(PropertyAccessor.GETTER, Visibility.NONE);
48+
MAPPER.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
49+
50+
String json = MAPPER.writeValueAsString(new RecordWithInterfaceWithGetter("Bob"));
51+
52+
assertEquals("{\"name\":\"Bob\"}", json);
53+
}
54+
}

0 commit comments

Comments
 (0)