Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add tuple type for list with fixed element types format #3597

Open
wants to merge 15 commits into
base: 2.14
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ protected void _addImplicitFactoryCreators(DeserializationContext ctxt,
}
continue;
}
AnnotatedParameter nonAnnotatedParam = null;
AnnotatedParameter nonAnnotatedParam = null;
SettableBeanProperty[] properties = new SettableBeanProperty[argCount];
int implicitNameCount = 0;
int explicitNameCount = 0;
Expand Down Expand Up @@ -1474,6 +1474,18 @@ public JsonDeserializer<?> createCollectionLikeDeserializer(DeserializationConte
return deser;
}

@Override
public JsonDeserializer<?> createTupleDeserializer(DeserializationContext ctxt,
TupleType type, BeanDescription beanDesc)
throws JsonMappingException
{
CollectionType ct = ctxt.getTypeFactory().constructCollectionType(Collection.class,
TypeFactory.unknownType());
CollectionDeserializer deser = (CollectionDeserializer) createCollectionDeserializer(ctxt,
ct, beanDesc);
return new TupleDeserializer(deser, type);
}

/*
/**********************************************************
/* DeserializerFactory impl: Map(-like) deserializers
Expand Down Expand Up @@ -2010,7 +2022,7 @@ public TypeDeserializer findPropertyTypeDeserializer(DeserializationConfig confi
throws JsonMappingException
{
AnnotationIntrospector ai = config.getAnnotationIntrospector();
TypeResolverBuilder<?> b = ai.findPropertyTypeResolver(config, annotated, baseType);
TypeResolverBuilder<?> b = ai.findPropertyTypeResolver(config, annotated, baseType);
// Defaulting: if no annotations on member, check value class
if (b == null) {
return findTypeDeserializer(config, baseType);
Expand All @@ -2037,13 +2049,13 @@ public TypeDeserializer findPropertyTypeDeserializer(DeserializationConfig confi
*
* @param containerType Type of property; must be a container type
* @param propertyEntity Field or method that contains container property
*/
*/
public TypeDeserializer findPropertyContentTypeDeserializer(DeserializationConfig config,
JavaType containerType, AnnotatedMember propertyEntity)
throws JsonMappingException
{
AnnotationIntrospector ai = config.getAnnotationIntrospector();
TypeResolverBuilder<?> b = ai.findPropertyContentTypeResolver(config, propertyEntity, containerType);
TypeResolverBuilder<?> b = ai.findPropertyContentTypeResolver(config, propertyEntity, containerType);
JavaType contentType = containerType.getContentType();
// Defaulting: if no annotations on member, check class
if (b == null) {
Expand Down Expand Up @@ -2086,13 +2098,17 @@ public JsonDeserializer<?> findDefaultDeserializer(DeserializationContext ctxt,
return StringDeserializer.instance;
}
if (rawType == CLASS_ITERABLE) {
// [databind#199]: Can and should 'upgrade' to a Collection type:
TypeFactory tf = ctxt.getTypeFactory();
JavaType[] tps = tf.findTypeParameters(type, CLASS_ITERABLE);
JavaType elemType = (tps == null || tps.length != 1) ? TypeFactory.unknownType() : tps[0];
CollectionType ct = tf.constructCollectionType(Collection.class, elemType);
// Should we re-introspect beanDesc? For now let's not...
return createCollectionDeserializer(ctxt, ct, beanDesc);
if (type instanceof TupleType) {
return createTupleDeserializer(ctxt, (TupleType) type, beanDesc);
} else {
// [databind#199]: Can and should 'upgrade' to a Collection type:
TypeFactory tf = ctxt.getTypeFactory();
JavaType[] tps = tf.findTypeParameters(type, CLASS_ITERABLE);
// Should we re-introspect beanDesc? For now let's not...
JavaType elemType = (tps == null || tps.length != 1) ? TypeFactory.unknownType() : tps[0];
CollectionType ct = tf.constructCollectionType(Collection.class, elemType);
return createCollectionDeserializer(ctxt, ct, beanDesc);
}
}
if (rawType == CLASS_MAP_ENTRY) {
// 28-Apr-2015, tatu: TypeFactory does it all for us already so
Expand Down Expand Up @@ -2375,7 +2391,7 @@ protected JavaType resolveMemberAndTypeAnnotations(DeserializationContext ctxt,
type = type.withContentValueHandler(cd);
}
TypeDeserializer contentTypeDeser = findPropertyContentTypeDeserializer(
ctxt.getConfig(), type, (AnnotatedMember) member);
ctxt.getConfig(), type, (AnnotatedMember) member);
if (contentTypeDeser != null) {
type = type.withContentTypeHandler(contentTypeDeser);
}
Expand Down Expand Up @@ -2418,7 +2434,7 @@ protected boolean _hasCreatorAnnotation(DeserializationContext ctxt,
AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
if (intr != null) {
JsonCreator.Mode mode = intr.findCreatorAnnotation(ctxt.getConfig(), ann);
return (mode != null) && (mode != JsonCreator.Mode.DISABLED);
return (mode != null) && (mode != JsonCreator.Mode.DISABLED);
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* {@link JsonDeserializer} instances (which are then cached by
* context and/or dedicated cache).
*<p>
* Since there are multiple broad categories of deserializers, there are
* Since there are multiple broad categories of deserializers, there are
* multiple factory methods:
*<ul>
* <li>For JSON "Array" type, we need 2 methods: one to deal with expected
Expand Down Expand Up @@ -165,6 +165,10 @@ public abstract JsonDeserializer<?> createCollectionLikeDeserializer(Deserializa
CollectionLikeType type, BeanDescription beanDesc)
throws JsonMappingException;

public abstract JsonDeserializer<?> createTupleDeserializer(DeserializationContext ctxt,
TupleType type, BeanDescription beanDesc)
throws JsonMappingException;

public abstract JsonDeserializer<?> createMapDeserializer(DeserializationContext ctxt,
MapType type, BeanDescription beanDesc)
throws JsonMappingException;
Expand Down Expand Up @@ -208,7 +212,7 @@ public abstract TypeDeserializer findTypeDeserializer(DeserializationConfig conf
* given type as opposed to auto-generated "Bean" deserializer. Factory itself will check
* for known JDK-provided types, but registered {@link com.fasterxml.jackson.databind.Module}s
* are also called to see if they might provide explicit deserializer.
*<p>
*<p>
* Main use for this method is with Safe Default Typing (and generally Safe Polymorphic
* Deserialization), during which it is good to be able to check that given raw type
* is explicitly supported and as such "known type" (as opposed to potentially
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package com.fasterxml.jackson.databind.deser.std;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.deser.NullValueProvider;
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.type.TupleType;
import com.fasterxml.jackson.databind.util.ClassUtil;

/**
* TupleDeserializer
*/
@JacksonStdImpl
public class TupleDeserializer extends CollectionDeserializer {
private static final long serialVersionUID = 1L;

private TupleType tupleType;

private List<JsonDeserializer<Object>> valueDeserList;

private List<NullValueProvider> nullerList;

public TupleDeserializer(CollectionDeserializer deser, TupleType tupleType)
{
// disable unwrapSingle.
super(deser._valueType,
deser._valueDeserializer, deser._valueTypeDeserializer,
deser._valueInstantiator, deser._delegateDeserializer,
deser._nullProvider, false);
this.tupleType = tupleType;
this.valueDeserList = new ArrayList<>();
this.nullerList = new ArrayList<>();
}

protected TupleDeserializer (JavaType containerType,
JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser,
ValueInstantiator valueInstantiator,
JsonDeserializer<Object> delegateDeser,
NullValueProvider nuller,
TupleType tupleType,
List<JsonDeserializer<Object>> valueDeserList, List<NullValueProvider> nullerList)
{
// disable unwrapSingle.
super(containerType, valueDeser, valueTypeDeser, valueInstantiator, delegateDeser,
nuller, false);
this.tupleType = tupleType;
this.valueDeserList = valueDeserList;
this.nullerList = nullerList;
}

@Override
protected Collection<Object> _deserializeFromArray(JsonParser p, DeserializationContext ctxt,
Collection<Object> result)
throws IOException
{
p.setCurrentValue(result);

JsonToken t;
int idx = 0;
while ((t = p.nextToken()) != JsonToken.END_ARRAY) {
try {
Object value;
if (t == JsonToken.VALUE_NULL) {
value = nullerList.get(idx++).getNullValue(ctxt);
} else {
value = valueDeserList.get(idx++).deserialize(p, ctxt);
}
result.add(value);
} catch (Exception e) {
boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
if (!wrap) {
ClassUtil.throwIfRTE(e);
}
throw JsonMappingException.wrapWithPath(e, result, result.size());
}
}
return result;
}

@Override
public CollectionDeserializer createContextual(DeserializationContext ctxt,
BeanProperty property) throws JsonMappingException
{
for (JavaType type : tupleType.getBindings().getTypeParameters()) {
JsonDeserializer<Object> valueDeser = ctxt.findContextualValueDeserializer(type, null);
NullValueProvider nuller = findContentNullProvider(ctxt, null, valueDeser);
this.valueDeserList.add(valueDeser);
this.nullerList.add(nuller);
}
return super.createContextual(ctxt, property);
}

@SuppressWarnings("unchecked")
@Override
protected TupleDeserializer withResolved(JsonDeserializer<?> dd,
JsonDeserializer<?> vd, TypeDeserializer vtd,
NullValueProvider nuller, Boolean unwrapSingle)
{
return new TupleDeserializer(_containerType,
(JsonDeserializer<Object>) vd, vtd,
_valueInstantiator, (JsonDeserializer<Object>) dd,
nuller,
this.tupleType,
this.valueDeserList, this.nullerList);
}
}
21 changes: 21 additions & 0 deletions src/main/java/com/fasterxml/jackson/databind/type/TupleType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.fasterxml.jackson.databind.type;

/**
* Tuple types are defined as list with fixed element types format.
*/
public class TupleType extends SimpleType {
private static final long serialVersionUID = 1L;

protected TupleType(SimpleType base)
{
super(base);
}

@Override
public String toString()
{
StringBuilder sb = new StringBuilder(40);
sb.append("[tuple type, class ").append(buildCanonicalName()).append(']');
return sb.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ private TypeBindings(String[] names, JavaType[] types, String[] uvars)
_hashCode = h;
}

private TypeBindings(String[] names, JavaType[] elementTypes)
{
_names = (names == null) ? NO_STRINGS : names;
_types = (elementTypes == null) ? NO_TYPES : elementTypes;
int h = 1;
for (int i = 0, len = _types.length; i < len; ++i) {
h += _types[i].hashCode();
}
_unboundVariables = null;
_hashCode = h;
}

public static TypeBindings emptyBindings() {
return EMPTY;
}
Expand Down Expand Up @@ -117,6 +129,15 @@ public static TypeBindings create(Class<?> erasedType, JavaType[] types)
return new TypeBindings(names, types, null);
}

public static TypeBindings createTuple(JavaType[] elementTypes)
{
if (elementTypes == null) {
elementTypes = NO_TYPES;
}
return new TypeBindings(new String[] { TypeParamStash.VARS_ITERABLE[0].getName() },
elementTypes);
}

public static TypeBindings create(Class<?> erasedType, JavaType typeArg1)
{
// 30-Oct-2015, tatu: Minor optimization for relatively common cases
Expand Down Expand Up @@ -260,7 +281,7 @@ public boolean isEmpty() {
/**
* Returns number of bindings contained
*/
public int size() {
public int size() {
return _types.length;
}

Expand Down Expand Up @@ -420,7 +441,7 @@ public static TypeVariable<?>[] paramsFor1(Class<?> erasedType)
return VARS_ITERABLE;
}
return erasedType.getTypeParameters();
}
}

public static TypeVariable<?>[] paramsFor2(Class<?> erasedType)
{
Expand All @@ -434,7 +455,7 @@ public static TypeVariable<?>[] paramsFor2(Class<?> erasedType)
return VARS_LINKED_HASH_MAP;
}
return erasedType.getTypeParameters();
}
}
}

/**
Expand Down
Loading