Skip to content

Commit 82d0af2

Browse files
authored
Merge branch '3.x' into 5405-map-format-shape-bug
2 parents 046dedd + 1e253a6 commit 82d0af2

28 files changed

+1257
-329
lines changed

release-notes/VERSION

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ Versions: 3.x (for earlier see VERSION-2.x)
1111
#1196: Add opt-in error collection for deserialization
1212
(requested by @odrotbohm)
1313
(contributed by @sri-adarsh-kumar)
14+
#1654: @JsonDeserialize(contentUsing=...) is ignored if content
15+
type is determined by @JsonTypeInfo
16+
(reported by @pdegoeje)
17+
(fix by @cowtowncoder, @JacksonJang)
1418
#1980: Add method `remove(JsonPointer)` in `ContainerNode`
1519
(fix by @cowtowncoder, w/ Claude code)
1620
#3964: Deserialization issue: MismatchedInputException, Bean not

src/main/java/tools/jackson/databind/BeanDescription.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,14 +198,22 @@ public AnnotatedMember findJsonKeyAccessor() {
198198
*/
199199
public abstract AnnotatedMember findJsonValueAccessor();
200200

201+
/**
202+
* Method used to locate the Method or Field of introspected class that
203+
* is annotated with {@link com.fasterxml.jackson.annotation.JsonAnyGetter}
204+
* (or equivalent annotation).
205+
* If no such {@code AnnotatedMember} exists {@code null} is returned.
206+
* If more than one are found, an exception is thrown.
207+
*/
201208
public abstract AnnotatedMember findAnyGetter();
202209

203210
/**
204211
* Method used to locate a mutator (settable field, or 2-argument set method)
205212
* of introspected class that
206-
* implements {@link com.fasterxml.jackson.annotation.JsonAnySetter}.
207-
* If no such mutator exists null is returned. If more than one are found,
208-
* an exception is thrown.
213+
* is annotated with {@link com.fasterxml.jackson.annotation.JsonAnySetter}
214+
* (or equivalent annotation).
215+
* If no such mutator exists {@code null} is returned.
216+
* If more than one are found an exception is thrown.
209217
* Additional checks are also made to see that method signature
210218
* is acceptable: needs to take 2 arguments, first one String or
211219
* Object; second any can be any type.

src/main/java/tools/jackson/databind/DatabindContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
/**
2424
* Shared base class for {@link DeserializationContext} and
2525
* {@link SerializationContext}, context objects passed through data-binding
26-
* process. Designed so that some of implementations can rely on shared
26+
* process. Designed so that some of the implementations can rely on shared
2727
* aspects like access to secondary contextual objects like type factories
2828
* or handler instantiators.
2929
*/

src/main/java/tools/jackson/databind/ObjectMapper.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,7 @@ protected Object readResolve() {
186186
*/
187187

188188
/**
189-
* Factory used for constructing per-call {@link SerializationContext}s.
190-
*<p>
191-
* Note: while serializers are only exposed {@link SerializationContext},
192-
* mappers and readers need to access additional API defined by
193-
* {@link SerializationContextExt}
189+
* Factory used for constructing per-call {@link SerializationContext} instances.
194190
*/
195191
protected final SerializationContexts _serializationContexts;
196192

@@ -207,7 +203,7 @@ protected Object readResolve() {
207203
*/
208204

209205
/**
210-
* Factory used for constructing per-call {@link DeserializationContext}s.
206+
* Factory used for constructing per-call {@link DeserializationContext} instances.
211207
*/
212208
protected final DeserializationContexts _deserializationContexts;
213209

src/main/java/tools/jackson/databind/ObjectWriter.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public class ObjectWriter
5454
* Factory used for constructing per-call {@link SerializationContext}s.
5555
*<p>
5656
* Note: while serializers are only exposed {@link SerializationContext},
57-
* mappers and readers need to access additional API defined by
57+
* writers need to access additional API defined by
5858
* {@link SerializationContextExt}
5959
*/
6060
protected final SerializationContexts _serializationContexts;
@@ -80,7 +80,7 @@ public class ObjectWriter
8080
* We may pre-fetch serializer if root type
8181
* is known (has been explicitly declared), and if so, reuse it afterwards.
8282
* This allows avoiding further serializer lookups and increases
83-
* performance a bit on cases where readers are reused.
83+
* performance a bit on cases where writers are reused.
8484
*/
8585
protected final Prefetch _prefetch;
8686

@@ -367,7 +367,7 @@ public ObjectWriter withoutFeatures(FormatFeature... features) {
367367
* as the root type for serialization, instead of runtime dynamic
368368
* type of the root object itself.
369369
*<p>
370-
* Note that method does NOT change state of this reader, but
370+
* Note that the method does NOT change the state of this writer, but
371371
* rather construct and returns a newly configured instance.
372372
*/
373373
public ObjectWriter forType(JavaType rootType) {
@@ -403,7 +403,7 @@ public ObjectWriter forType(TypeReference<?> rootType) {
403403
* use specified date format for serializing dates; or if null passed, one
404404
* that will serialize dates as numeric timestamps.
405405
*<p>
406-
* Note that the method does NOT change state of this reader, but
406+
* Note that the method does NOT change the state of this writer, but
407407
* rather construct and returns a newly configured instance.
408408
*/
409409
public ObjectWriter with(DateFormat df) {
@@ -442,7 +442,7 @@ public ObjectWriter with(PrettyPrinter pp) {
442442
* specifies what root name to use for "root element wrapping".
443443
* See {@link SerializationConfig#withRootName(String)} for details.
444444
*<p>
445-
* Note that method does NOT change state of this reader, but
445+
* Note that the method does NOT change the state of this writer, but
446446
* rather construct and returns a newly configured instance.
447447
*
448448
* @param rootName Root name to use, if non-empty; `null` for "use defaults",
@@ -472,7 +472,7 @@ public ObjectWriter withoutRootName() {
472472
* Method that will construct a new instance that uses specific format schema
473473
* for serialization.
474474
*<p>
475-
* Note that method does NOT change state of this reader, but
475+
* Note that the method does NOT change the state of this writer, but
476476
* rather construct and returns a newly configured instance.
477477
*/
478478
public ObjectWriter with(FormatSchema schema) {
@@ -485,7 +485,7 @@ public ObjectWriter with(FormatSchema schema) {
485485
* serialization view for serialization (with null basically disables
486486
* view processing)
487487
*<p>
488-
* Note that the method does NOT change state of this reader, but
488+
* Note that the method does NOT change the state of this writer, but
489489
* rather construct and returns a newly configured instance.
490490
*/
491491
public ObjectWriter withView(Class<?> view) {
@@ -1279,7 +1279,7 @@ public final static class Prefetch
12791279
* We may pre-fetch serializer if {@link #rootType}
12801280
* is known, and if so, reuse it afterwards.
12811281
* This allows avoiding further serializer lookups and increases
1282-
* performance a bit on cases where readers are reused.
1282+
* performance a bit on cases where writers are reused.
12831283
*/
12841284
private final ValueSerializer<Object> valueSerializer;
12851285

src/main/java/tools/jackson/databind/ValueSerializer.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.Set;
55

66
import com.fasterxml.jackson.annotation.JsonFormat;
7+
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
78

89
import tools.jackson.core.*;
910
import tools.jackson.databind.jsonFormatVisitors.JsonFormatVisitable;
@@ -252,13 +253,20 @@ public void serializeWithType(T value, JsonGenerator gen, SerializationContext c
252253
TypeSerializer typeSer)
253254
throws JacksonException
254255
{
256+
// 07-Dec-2025, tatu: [databind#1654] Check for "no-op" type serializer
257+
// indirectly
258+
if (typeSer.getTypeInclusion() == As.NOTHING) {
259+
serialize(value, gen, ctxt);
260+
return;
261+
}
262+
255263
Class<?> clz = handledType();
256264
if (clz == null) {
257265
clz = value.getClass();
258266
}
259267
ctxt.reportBadDefinition(clz, String.format(
260-
"Type id handling not implemented for type %s (by serializer of type %s)",
261-
clz.getName(), getClass().getName()));
268+
"Type id handling (method `serializeWithType()`) not implemented for type %s (by serializer of type %s)",
269+
ClassUtil.nameOf(clz), ClassUtil.nameOf(getClass())));
262270
}
263271

264272
/*

src/main/java/tools/jackson/databind/cfg/MapperBuilder.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,8 @@ public B withAllCoercionConfigs(Consumer<CoercionConfigs> handler) {
10371037
*/
10381038
public B removeAllModules() {
10391039
_modules = null;
1040+
// [databind#5481]: invalidate cached state when modules are modified
1041+
_savedState = null;
10401042
return _this();
10411043
}
10421044

@@ -1068,6 +1070,8 @@ public B addModule(JacksonModule module)
10681070
_modules.putIfAbsent(dep.getRegistrationId(), dep);
10691071
}
10701072
_modules.put(moduleId, module);
1073+
// [databind#5481]: invalidate cached state when modules are modified
1074+
_savedState = null;
10711075
return _this();
10721076
}
10731077

src/main/java/tools/jackson/databind/cfg/SerializationContexts.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@
1010

1111
/**
1212
* Factory/builder class that replaces Jackson 2.x concept of "blueprint" instance
13-
* of {@link tools.jackson.databind.SerializationContext}. It will be constructed and configured during
14-
* {@link ObjectMapper} building phase, and will be called once per {@code writeValue}
15-
* call to construct actual stateful {@link tools.jackson.databind.SerializationContext} to use during
16-
* serialization.
13+
* of {@link tools.jackson.databind.SerializationContext}. It will be constructed
14+
* and configured during {@link ObjectMapper} building phase,
15+
* and will be called once per {@code writeValue} call to construct actual stateful
16+
* {@link tools.jackson.databind.SerializationContext} to use during serialization.
1717
*<p>
1818
* Note that since this object has to be serializable (to allow JDK serialization of
19-
* mapper instances), {@link tools.jackson.databind.SerializationContext} need not be serializable any more.
19+
* mapper instances), {@link tools.jackson.databind.SerializationContext}
20+
* need not be serializable any more.
2021
*
2122
* @since 3.0
2223
*/

src/main/java/tools/jackson/databind/deser/BasicDeserializerFactory.java

Lines changed: 35 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import tools.jackson.databind.ext.jdk8.OptionalLongDeserializer;
2525
import tools.jackson.databind.introspect.*;
2626
import tools.jackson.databind.jsontype.TypeDeserializer;
27+
import tools.jackson.databind.jsontype.impl.NoOpTypeDeserializer;
2728
import tools.jackson.databind.type.*;
2829
import tools.jackson.databind.util.*;
2930

@@ -714,17 +715,14 @@ public ValueDeserializer<?> createArrayDeserializer(DeserializationContext ctxt,
714715
ArrayType type, BeanDescription.Supplier beanDescRef)
715716
{
716717
final DeserializationConfig config = ctxt.getConfig();
717-
JavaType elemType = type.getContentType();
718+
final JavaType elemType = type.getContentType();
718719

719720
// Very first thing: is deserializer hard-coded for elements?
720721
@SuppressWarnings("unchecked")
721-
ValueDeserializer<Object> contentDeser = (ValueDeserializer<Object>) elemType.getValueHandler();
722+
final ValueDeserializer<Object> contentDeser = (ValueDeserializer<Object>) elemType.getValueHandler();
722723
// Then optional type info: if type has been resolved, we may already know type deserializer:
723-
TypeDeserializer elemTypeDeser = (TypeDeserializer) elemType.getTypeHandler();
724-
// but if not, may still be possible to find:
725-
if (elemTypeDeser == null) {
726-
elemTypeDeser = ctxt.findTypeDeserializer(elemType);
727-
}
724+
final TypeDeserializer elemTypeDeser = _findContentTypeDeserializer(ctxt, elemType);
725+
728726
// 23-Nov-2010, tatu: Custom array deserializer?
729727
ValueDeserializer<?> deser = _findCustomArrayDeserializer(type,
730728
config, beanDescRef, elemTypeDeser, contentDeser);
@@ -760,17 +758,11 @@ public ValueDeserializer<?> createArrayDeserializer(DeserializationContext ctxt,
760758
public ValueDeserializer<?> createCollectionDeserializer(DeserializationContext ctxt,
761759
CollectionType type, BeanDescription.Supplier beanDescRef)
762760
{
763-
JavaType contentType = type.getContentType();
764-
// Very first thing: is deserializer hard-coded for elements?
765-
ValueDeserializer<Object> contentDeser = (ValueDeserializer<Object>) contentType.getValueHandler();
761+
final JavaType contentType = type.getContentType();
762+
final ValueDeserializer<Object> contentDeser = (ValueDeserializer<Object>) contentType.getValueHandler();
763+
final TypeDeserializer contentTypeDeser = _findContentTypeDeserializer(ctxt, contentType);
766764
final DeserializationConfig config = ctxt.getConfig();
767765

768-
// Then optional type info: if type has been resolved, we may already know type deserializer:
769-
TypeDeserializer contentTypeDeser = (TypeDeserializer) contentType.getTypeHandler();
770-
// but if not, may still be possible to find:
771-
if (contentTypeDeser == null) {
772-
contentTypeDeser = ctxt.findTypeDeserializer(contentType);
773-
}
774766
// 23-Nov-2010, tatu: Custom deserializer?
775767
ValueDeserializer<?> deser = _findCustomCollectionDeserializer(type,
776768
config, beanDescRef, contentTypeDeser, contentDeser);
@@ -853,18 +845,13 @@ protected CollectionType _mapAbstractCollectionType(JavaType type, Deserializati
853845
public ValueDeserializer<?> createCollectionLikeDeserializer(DeserializationContext ctxt,
854846
CollectionLikeType type, BeanDescription.Supplier beanDescRef)
855847
{
856-
JavaType contentType = type.getContentType();
848+
final JavaType contentType = type.getContentType();
857849
// Very first thing: is deserializer hard-coded for elements?
858850
@SuppressWarnings("unchecked")
859851
ValueDeserializer<Object> contentDeser = (ValueDeserializer<Object>) contentType.getValueHandler();
860852
final DeserializationConfig config = ctxt.getConfig();
853+
final TypeDeserializer contentTypeDeser = _findContentTypeDeserializer(ctxt, contentType);
861854

862-
// Then optional type info: if type has been resolved, we may already know type deserializer:
863-
TypeDeserializer contentTypeDeser = (TypeDeserializer)contentType.getTypeHandler();
864-
// but if not, may still be possible to find:
865-
if (contentTypeDeser == null) {
866-
contentTypeDeser = ctxt.findTypeDeserializer(contentType);
867-
}
868855
ValueDeserializer<?> deser = _findCustomCollectionLikeDeserializer(type, config, beanDescRef,
869856
contentTypeDeser, contentDeser);
870857
if (deser != null) {
@@ -889,8 +876,8 @@ public ValueDeserializer<?> createMapDeserializer(DeserializationContext ctxt,
889876
MapType type, BeanDescription.Supplier beanDescRef)
890877
{
891878
final DeserializationConfig config = ctxt.getConfig();
892-
JavaType keyType = type.getKeyType();
893-
JavaType contentType = type.getContentType();
879+
final JavaType keyType = type.getKeyType();
880+
final JavaType contentType = type.getContentType();
894881

895882
// First: is there annotation-specified deserializer for values?
896883
@SuppressWarnings("unchecked")
@@ -899,11 +886,7 @@ public ValueDeserializer<?> createMapDeserializer(DeserializationContext ctxt,
899886
// Ok: need a key deserializer (null indicates 'default' here)
900887
KeyDeserializer keyDes = (KeyDeserializer) keyType.getValueHandler();
901888
// Then optional type info; either attached to type, or resolved separately:
902-
TypeDeserializer contentTypeDeser = (TypeDeserializer) contentType.getTypeHandler();
903-
// but if not, may still be possible to find:
904-
if (contentTypeDeser == null) {
905-
contentTypeDeser = ctxt.findTypeDeserializer(contentType);
906-
}
889+
final TypeDeserializer contentTypeDeser = _findContentTypeDeserializer(ctxt, contentType);
907890

908891
// 23-Nov-2010, tatu: Custom deserializer?
909892
ValueDeserializer<?> deser = _findCustomMapDeserializer(type, config, beanDescRef,
@@ -1007,8 +990,8 @@ protected MapType _mapAbstractMapType(JavaType type, DeserializationConfig confi
1007990
public ValueDeserializer<?> createMapLikeDeserializer(DeserializationContext ctxt,
1008991
MapLikeType type, BeanDescription.Supplier beanDescRef)
1009992
{
1010-
JavaType keyType = type.getKeyType();
1011-
JavaType contentType = type.getContentType();
993+
final JavaType keyType = type.getKeyType();
994+
final JavaType contentType = type.getContentType();
1012995
final DeserializationConfig config = ctxt.getConfig();
1013996

1014997
// First: is there annotation-specified deserializer for values?
@@ -1023,11 +1006,7 @@ public ValueDeserializer<?> createMapLikeDeserializer(DeserializationContext ctx
10231006
}
10241007
*/
10251008
// Then optional type info; either attached to type, or resolve separately:
1026-
TypeDeserializer contentTypeDeser = (TypeDeserializer) contentType.getTypeHandler();
1027-
// but if not, may still be possible to find:
1028-
if (contentTypeDeser == null) {
1029-
contentTypeDeser = ctxt.findTypeDeserializer(contentType);
1030-
}
1009+
final TypeDeserializer contentTypeDeser = _findContentTypeDeserializer(ctxt, contentType);
10311010
ValueDeserializer<?> deser = _findCustomMapLikeDeserializer(type, config,
10321011
beanDescRef, keyDes, contentTypeDeser, contentDeser);
10331012
if (deser != null) {
@@ -1139,11 +1118,7 @@ public ValueDeserializer<?> createReferenceDeserializer(DeserializationContext c
11391118
@SuppressWarnings("unchecked")
11401119
ValueDeserializer<Object> contentDeser = (ValueDeserializer<Object>) contentType.getValueHandler();
11411120
final DeserializationConfig config = ctxt.getConfig();
1142-
// Then optional type info: if type has been resolved, we may already know type deserializer:
1143-
TypeDeserializer contentTypeDeser = (TypeDeserializer) contentType.getTypeHandler();
1144-
if (contentTypeDeser == null) { // or if not, may be able to find:
1145-
contentTypeDeser = ctxt.findTypeDeserializer(contentType);
1146-
}
1121+
final TypeDeserializer contentTypeDeser = _findContentTypeDeserializer(ctxt, contentType);
11471122
ValueDeserializer<?> deser = _findCustomReferenceDeserializer(type, config, beanDescRef,
11481123
contentTypeDeser, contentDeser);
11491124

@@ -1738,6 +1713,24 @@ protected boolean _hasCreatorAnnotation(MapperConfig<?> config,
17381713
return false;
17391714
}
17401715

1716+
// @since 3.1
1717+
protected TypeDeserializer _findContentTypeDeserializer(DeserializationContext ctxt,
1718+
JavaType contentType)
1719+
{
1720+
// Then optional type info: if type has been resolved, we may already know type deserializer:
1721+
TypeDeserializer contentTypeDeser = (TypeDeserializer) contentType.getTypeHandler();
1722+
// [databind#1654]: @JsonTypeInfo(use = Id.NONE) should not apply type deserializer
1723+
// when custom content deserializer is specified via @JsonDeserialize(contentUsing = ...)
1724+
if (contentTypeDeser instanceof NoOpTypeDeserializer) {
1725+
return null;
1726+
}
1727+
if (contentTypeDeser == null) {
1728+
// but if not, may still be possible to find:
1729+
contentTypeDeser = ctxt.findTypeDeserializer(contentType);
1730+
}
1731+
return contentTypeDeser;
1732+
}
1733+
17411734
/*
17421735
/**********************************************************************
17431736
/* Helper classes

0 commit comments

Comments
 (0)