Skip to content

Commit 4349557

Browse files
committed
Fix #1654
1 parent 57ee5f3 commit 4349557

File tree

4 files changed

+217
-4
lines changed

4 files changed

+217
-4
lines changed

src/main/java/tools/jackson/databind/jsontype/TypeResolverProvider.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ public TypeSerializer findPropertyTypeSerializer(SerializationContext ctxt,
113113
if (b == null) {
114114
return findTypeSerializer(ctxt, baseType, ctxt.introspectClassAnnotations(baseType));
115115
}
116+
// [databind#1654]: Explicit `@JsonTypeInfo(Id.NONE)` should block class-level type info
117+
if (b == NO_RESOLVER) {
118+
return null;
119+
}
116120
Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByClass(
117121
config, accessor, baseType);
118122
// 10-Jun-2015, tatu: Since not created for Bean Property, no need for post-processing
@@ -133,6 +137,10 @@ public TypeDeserializer findPropertyTypeDeserializer(DeserializationContext ctxt
133137
if (b == null) {
134138
return findTypeDeserializer(ctxt, baseType, ctxt.introspectClassAnnotations(baseType));
135139
}
140+
// [databind#1654]: Explicit `@JsonTypeInfo(Id.NONE)` should block class-level type info
141+
if (b == NO_RESOLVER) {
142+
return null;
143+
}
136144
Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(config,
137145
accessor, baseType);
138146
// May need to figure out default implementation, if none found yet
@@ -162,6 +170,10 @@ public TypeSerializer findPropertyContentTypeSerializer(SerializationContext ctx
162170
return findTypeSerializer(ctxt, contentType,
163171
ctxt.introspectClassAnnotations(contentType.getRawClass()));
164172
}
173+
// [databind#1654]: Explicit `@JsonTypeInfo(Id.NONE)` should block class-level type info
174+
if (b == NO_RESOLVER) {
175+
return tools.jackson.databind.jsontype.impl.NoOpTypeSerializer.instance();
176+
}
165177
Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByClass(
166178
config, accessor, contentType);
167179
return b.buildTypeSerializer(ctxt, contentType, subtypes);
@@ -181,6 +193,10 @@ public TypeDeserializer findPropertyContentTypeDeserializer(DeserializationConte
181193
if (b == null) {
182194
return findTypeDeserializer(ctxt, contentType, ctxt.introspectClassAnnotations(contentType));
183195
}
196+
// [databind#1654]: Explicit `@JsonTypeInfo(Id.NONE)` should block class-level type info
197+
if (b == NO_RESOLVER) {
198+
return new tools.jackson.databind.jsontype.impl.NoOpTypeDeserializer(contentType);
199+
}
184200
Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(config,
185201
accessor, contentType);
186202
// May need to figure out default implementation, if none found yet
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package tools.jackson.databind.jsontype.impl;
2+
3+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
4+
5+
import tools.jackson.core.*;
6+
import tools.jackson.databind.*;
7+
import tools.jackson.databind.jsontype.TypeDeserializer;
8+
import tools.jackson.databind.jsontype.TypeIdResolver;
9+
10+
/**
11+
* Special {@link TypeDeserializer} implementation used to explicitly
12+
* block type deserialization. This is used when a property or class
13+
* is annotated with {@code @JsonTypeInfo(use = Id.NONE)}, indicating
14+
* that type information should not be expected or processed even if
15+
* the value type has a class-level type info annotation.
16+
*<p>
17+
* Unlike returning {@code null} (which means "no special type handling,
18+
* use defaults"), this actively prevents type information from being read.
19+
*
20+
* @since 3.1
21+
*/
22+
public class NoOpTypeDeserializer extends TypeDeserializer
23+
{
24+
private final JavaType _baseType;
25+
private final ValueDeserializer<Object> _defaultDeserializer;
26+
27+
public NoOpTypeDeserializer(JavaType baseType) {
28+
_baseType = baseType;
29+
_defaultDeserializer = null;
30+
}
31+
32+
private NoOpTypeDeserializer(JavaType baseType,
33+
ValueDeserializer<Object> defaultDeserializer) {
34+
_baseType = baseType;
35+
_defaultDeserializer = defaultDeserializer;
36+
}
37+
38+
public NoOpTypeDeserializer withDefaultImpl(ValueDeserializer<Object> deser) {
39+
if (_defaultDeserializer == deser) {
40+
return this;
41+
}
42+
return new NoOpTypeDeserializer(_baseType, deser);
43+
}
44+
45+
@Override
46+
public TypeDeserializer forProperty(BeanProperty prop) {
47+
return this;
48+
}
49+
50+
@Override
51+
public JsonTypeInfo.As getTypeInclusion() {
52+
return JsonTypeInfo.As.PROPERTY;
53+
}
54+
55+
@Override
56+
public String getPropertyName() {
57+
return null;
58+
}
59+
60+
@Override
61+
public TypeIdResolver getTypeIdResolver() {
62+
return null;
63+
}
64+
65+
@Override
66+
public Class<?> getDefaultImpl() {
67+
return null;
68+
}
69+
70+
@Override
71+
public Object deserializeTypedFromObject(JsonParser p,
72+
DeserializationContext ctxt) throws JacksonException
73+
{
74+
// Just deserialize without type info
75+
return _deserialize(p, ctxt);
76+
}
77+
78+
@Override
79+
public Object deserializeTypedFromArray(JsonParser p,
80+
DeserializationContext ctxt) throws JacksonException
81+
{
82+
// Just deserialize without type info
83+
return _deserialize(p, ctxt);
84+
}
85+
86+
@Override
87+
public Object deserializeTypedFromScalar(JsonParser p,
88+
DeserializationContext ctxt) throws JacksonException
89+
{
90+
// Just deserialize without type info
91+
return _deserialize(p, ctxt);
92+
}
93+
94+
@Override
95+
public Object deserializeTypedFromAny(JsonParser p,
96+
DeserializationContext ctxt) throws JacksonException
97+
{
98+
// Just deserialize without type info
99+
return _deserialize(p, ctxt);
100+
}
101+
102+
protected Object _deserialize(JsonParser p, DeserializationContext ctxt)
103+
throws JacksonException
104+
{
105+
if (_defaultDeserializer != null) {
106+
return _defaultDeserializer.deserialize(p, ctxt);
107+
}
108+
// Find deserializer for the base type (this will find custom deserializers
109+
// registered for this type, including those from @JsonDeserialize annotations)
110+
ValueDeserializer<Object> deser = ctxt.findContextualValueDeserializer(_baseType, null);
111+
if (deser == null) {
112+
ctxt.reportBadDefinition(_baseType,
113+
"Cannot find deserializer for type " + _baseType);
114+
}
115+
return deser.deserialize(p, ctxt);
116+
}
117+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package tools.jackson.databind.jsontype.impl;
2+
3+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
4+
5+
import tools.jackson.core.*;
6+
import tools.jackson.core.type.WritableTypeId;
7+
import tools.jackson.databind.BeanProperty;
8+
import tools.jackson.databind.SerializationContext;
9+
import tools.jackson.databind.jsontype.TypeIdResolver;
10+
import tools.jackson.databind.jsontype.TypeSerializer;
11+
12+
/**
13+
* Special {@link TypeSerializer} implementation used to explicitly
14+
* block type serialization. This is used when a property or class
15+
* is annotated with {@code @JsonTypeInfo(use = Id.NONE)}, indicating
16+
* that type information should not be included even if the value type
17+
* has a class-level type info annotation.
18+
*<p>
19+
* Unlike returning {@code null} (which means "no special type handling,
20+
* use defaults"), this actively prevents type information from being written.
21+
*
22+
* @since 3.1
23+
*/
24+
public class NoOpTypeSerializer extends TypeSerializer
25+
{
26+
private static final NoOpTypeSerializer INSTANCE = new NoOpTypeSerializer();
27+
28+
private NoOpTypeSerializer() { }
29+
30+
public static NoOpTypeSerializer instance() {
31+
return INSTANCE;
32+
}
33+
34+
@Override
35+
public TypeSerializer forProperty(SerializationContext ctxt, BeanProperty prop) {
36+
return this;
37+
}
38+
39+
@Override
40+
public JsonTypeInfo.As getTypeInclusion() {
41+
return JsonTypeInfo.As.PROPERTY;
42+
}
43+
44+
@Override
45+
public String getPropertyName() {
46+
return null;
47+
}
48+
49+
@Override
50+
public TypeIdResolver getTypeIdResolver() {
51+
return null;
52+
}
53+
54+
@Override
55+
public WritableTypeId writeTypePrefix(JsonGenerator g,
56+
SerializationContext ctxt, WritableTypeId typeId)
57+
throws JacksonException
58+
{
59+
// Write the value start token if needed, but NO type information
60+
if (typeId.valueShape == JsonToken.START_OBJECT) {
61+
g.writeStartObject(typeId.forValue);
62+
} else if (typeId.valueShape == JsonToken.START_ARRAY) {
63+
g.writeStartArray();
64+
}
65+
// Mark as "already written" to prevent suffix from trying to close
66+
typeId.wrapperWritten = false;
67+
return typeId;
68+
}
69+
70+
@Override
71+
public WritableTypeId writeTypeSuffix(JsonGenerator g,
72+
SerializationContext ctxt, WritableTypeId typeId)
73+
throws JacksonException
74+
{
75+
// Write the value end token if needed, but NO type information
76+
if (typeId.valueShape == JsonToken.START_OBJECT) {
77+
g.writeEndObject();
78+
} else if (typeId.valueShape == JsonToken.START_ARRAY) {
79+
g.writeEndArray();
80+
}
81+
return typeId;
82+
}
83+
}

src/test/java/tools/jackson/databind/tofix/NoTypeInfo1654Test.java renamed to src/test/java/tools/jackson/databind/jsontype/NoTypeInfo1654Test.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package tools.jackson.databind.tofix;
1+
package tools.jackson.databind.jsontype;
22

33
import java.util.*;
44

@@ -10,7 +10,6 @@
1010
import tools.jackson.databind.*;
1111
import tools.jackson.databind.annotation.JsonDeserialize;
1212
import tools.jackson.databind.testutil.DatabindTestUtil;
13-
import tools.jackson.databind.testutil.failure.JacksonTestFailureExpected;
1413

1514
import static org.junit.jupiter.api.Assertions.assertEquals;
1615

@@ -77,7 +76,6 @@ void noTypeElementOverride() throws Exception {
7776
}
7877

7978
// [databind#1654]
80-
@JacksonTestFailureExpected
8179
@Test
8280
void noTypeInfoOverrideSer() throws Exception {
8381
Value1654UntypedContainer cont = new Value1654UntypedContainer(
@@ -89,7 +87,6 @@ void noTypeInfoOverrideSer() throws Exception {
8987
}
9088

9189
// [databind#1654]
92-
@JacksonTestFailureExpected
9390
@Test
9491
void noTypeInfoOverrideDeser() throws Exception {
9592
// and then actual failing case

0 commit comments

Comments
 (0)