Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ Versions: 3.x (for earlier see VERSION-2.x)
#1196: Add opt-in error collection for deserialization
(requested by @odrotbohm)
(contributed by @sri-adarsh-kumar)
#1654: @JsonDeserialize(contentUsing=...) is ignored if content
type is determined by @JsonTypeInfo
(reported by @pdegoeje)
#1980: Add method `remove(JsonPointer)` in `ContainerNode`
(fix by @cowtowncoder, w/ Claude code)
#3964: Deserialization issue: MismatchedInputException, Bean not
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import tools.jackson.databind.ext.jdk8.OptionalLongDeserializer;
import tools.jackson.databind.introspect.*;
import tools.jackson.databind.jsontype.TypeDeserializer;
import tools.jackson.databind.jsontype.TypeResolverBuilder;
import tools.jackson.databind.type.*;
import tools.jackson.databind.util.*;

Expand Down Expand Up @@ -771,6 +772,7 @@ public ValueDeserializer<?> createCollectionDeserializer(DeserializationContext
if (contentTypeDeser == null) {
contentTypeDeser = ctxt.findTypeDeserializer(contentType);
}

// 23-Nov-2010, tatu: Custom deserializer?
ValueDeserializer<?> deser = _findCustomCollectionDeserializer(type,
config, beanDescRef, contentTypeDeser, contentDeser);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import tools.jackson.databind.introspect.Annotated;
import tools.jackson.databind.introspect.AnnotatedClass;
import tools.jackson.databind.introspect.AnnotatedMember;
import tools.jackson.databind.jsontype.impl.NoOpTypeDeserializer;
import tools.jackson.databind.jsontype.impl.NoOpTypeSerializer;
import tools.jackson.databind.jsontype.impl.StdTypeResolverBuilder;

/**
Expand Down Expand Up @@ -113,6 +115,10 @@ public TypeSerializer findPropertyTypeSerializer(SerializationContext ctxt,
if (b == null) {
return findTypeSerializer(ctxt, baseType, ctxt.introspectClassAnnotations(baseType));
}
// [databind#1654]: Explicit `@JsonTypeInfo(Id.NONE)` should block class-level type info
if (b == NO_RESOLVER) {
return null;
}
Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByClass(
config, accessor, baseType);
// 10-Jun-2015, tatu: Since not created for Bean Property, no need for post-processing
Expand All @@ -133,6 +139,10 @@ public TypeDeserializer findPropertyTypeDeserializer(DeserializationContext ctxt
if (b == null) {
return findTypeDeserializer(ctxt, baseType, ctxt.introspectClassAnnotations(baseType));
}
// [databind#1654]: Explicit `@JsonTypeInfo(Id.NONE)` should block class-level type info
if (b == NO_RESOLVER) {
return null;
}
Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(config,
accessor, baseType);
// May need to figure out default implementation, if none found yet
Expand Down Expand Up @@ -162,6 +172,10 @@ public TypeSerializer findPropertyContentTypeSerializer(SerializationContext ctx
return findTypeSerializer(ctxt, contentType,
ctxt.introspectClassAnnotations(contentType.getRawClass()));
}
// [databind#1654]: Explicit `@JsonTypeInfo(Id.NONE)` should block class-level type info
if (b == NO_RESOLVER) {
return NoOpTypeSerializer.instance();
}
Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByClass(
config, accessor, contentType);
return b.buildTypeSerializer(ctxt, contentType, subtypes);
Expand All @@ -181,6 +195,10 @@ public TypeDeserializer findPropertyContentTypeDeserializer(DeserializationConte
if (b == null) {
return findTypeDeserializer(ctxt, contentType, ctxt.introspectClassAnnotations(contentType));
}
// [databind#1654]: Explicit `@JsonTypeInfo(Id.NONE)` should block class-level type info
if (b == NO_RESOLVER) {
return NoOpTypeDeserializer.forBaseType(ctxt, contentType);
}
Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByTypeId(config,
accessor, contentType);
// May need to figure out default implementation, if none found yet
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package tools.jackson.databind.jsontype.impl;

import com.fasterxml.jackson.annotation.JsonTypeInfo;

import tools.jackson.core.*;
import tools.jackson.databind.*;
import tools.jackson.databind.jsontype.TypeDeserializer;
import tools.jackson.databind.jsontype.TypeIdResolver;
import tools.jackson.databind.util.ClassUtil;

/**
* Special {@link TypeDeserializer} implementation used to explicitly
* block type deserialization. This is used when a property or class
* is annotated with {@code @JsonTypeInfo(use = Id.NONE)}, indicating
* that type information should not be expected or processed even if
* the value type has a class-level type info annotation.
*<p>
* Unlike returning {@code null} (which means "no special type handling,
* use defaults"), this actively prevents type information from being read.
*
* @since 3.1
*/
public class NoOpTypeDeserializer extends TypeDeserializer
{
private final JavaType _baseType;
private final BeanProperty _property;

// Dynamically constructed deserializer
private volatile ValueDeserializer<Object> _deserializer;

private NoOpTypeDeserializer(JavaType baseType, BeanProperty prop) {
_baseType = baseType;
_property = prop;
}

public static NoOpTypeDeserializer forBaseType(DeserializationContext ctxt,
JavaType baseType) {
return new NoOpTypeDeserializer(baseType, null);
}

@Override
public TypeDeserializer forProperty(BeanProperty prop) {
if (_property == prop) {
return this;
}
return new NoOpTypeDeserializer(_baseType, prop);
}

@Override
public JsonTypeInfo.As getTypeInclusion() {
// No proper value but need to return something
return JsonTypeInfo.As.EXISTING_PROPERTY;
}

@Override
public String getPropertyName() {
return null;
}

@Override
public TypeIdResolver getTypeIdResolver() {
return null;
}

@Override
public Class<?> getDefaultImpl() {
return null;
}

@Override
public Object deserializeTypedFromObject(JsonParser p, DeserializationContext ctxt)
throws JacksonException
{
return _deserialize(p, ctxt);
}

@Override
public Object deserializeTypedFromArray(JsonParser p, DeserializationContext ctxt)
throws JacksonException
{
return _deserialize(p, ctxt);
}

@Override
public Object deserializeTypedFromScalar(JsonParser p, DeserializationContext ctxt)
throws JacksonException
{
return _deserialize(p, ctxt);
}

@Override
public Object deserializeTypedFromAny(JsonParser p, DeserializationContext ctxt)
throws JacksonException
{
return _deserialize(p, ctxt);
}

protected Object _deserialize(JsonParser p, DeserializationContext ctxt)
throws JacksonException
{
ValueDeserializer<Object> deser = _deserializer;

// Find deserializer for the base type, given property (if any).
// This will find custom deserializers registered for this type,
// including those from @JsonDeserialize annotations)
if (deser == null) {
deser = ctxt.findContextualValueDeserializer(_baseType, _property);
if (deser == null) {
ctxt.reportBadDefinition(_baseType,
"Cannot find deserializer for type " +ClassUtil.getTypeDescription(_baseType));
}
_deserializer = deser;
}
return deser.deserialize(p, ctxt);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package tools.jackson.databind.jsontype.impl;

import com.fasterxml.jackson.annotation.JsonTypeInfo;

import tools.jackson.core.*;
import tools.jackson.core.type.WritableTypeId;
import tools.jackson.databind.BeanProperty;
import tools.jackson.databind.SerializationContext;
import tools.jackson.databind.jsontype.TypeIdResolver;
import tools.jackson.databind.jsontype.TypeSerializer;

/**
* Special {@link TypeSerializer} implementation used to explicitly
* block type serialization. This is used when a property or class
* is annotated with {@code @JsonTypeInfo(use = Id.NONE)}, indicating
* that type information should not be included even if the value type
* has a class-level type info annotation.
*<p>
* Unlike returning {@code null} (which means "no special type handling,
* use defaults"), this actively prevents type information from being written.
*
* @since 3.1
*/
public class NoOpTypeSerializer extends TypeSerializer
{
private static final NoOpTypeSerializer INSTANCE = new NoOpTypeSerializer();

private NoOpTypeSerializer() { }

public static NoOpTypeSerializer instance() {
return INSTANCE;
}

@Override
public TypeSerializer forProperty(SerializationContext ctxt, BeanProperty prop) {
return this;
}

@Override
public JsonTypeInfo.As getTypeInclusion() {
// No proper one to use but must return something:
return JsonTypeInfo.As.EXISTING_PROPERTY;
}

@Override
public String getPropertyName() {
return null;
}

@Override
public TypeIdResolver getTypeIdResolver() {
return null;
}

@Override
public WritableTypeId writeTypePrefix(JsonGenerator g,
SerializationContext ctxt, WritableTypeId typeId)
throws JacksonException
{
// Write the value start token if needed, but NO type information
if (typeId.valueShape == JsonToken.START_OBJECT) {
g.writeStartObject(typeId.forValue);
} else if (typeId.valueShape == JsonToken.START_ARRAY) {
g.writeStartArray();
}
// 1. Start marker (part of value) was written but
// 2. No value wrapper was written.
typeId.wrapperWritten = false;
return typeId;
}

@Override
public WritableTypeId writeTypeSuffix(JsonGenerator g,
SerializationContext ctxt, WritableTypeId typeId)
throws JacksonException
{
// Write the value end token if needed, but no wrapper to close
if (typeId.valueShape == JsonToken.START_OBJECT) {
g.writeEndObject();
} else if (typeId.valueShape == JsonToken.START_ARRAY) {
g.writeEndArray();
}
return typeId;
}
}
Loading
Loading