Skip to content

Commit

Permalink
Fix #1565
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Apr 6, 2018
1 parent 170a414 commit 09e5ba1
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 9 deletions.
5 changes: 5 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ Project: jackson-databind
=== Releases ===
------------------------------------------------------------------------

2.9.6 (not yet released)

#1565: Deserialization failure with Polymorphism using JsonTypeInfo `defaultImpl`,
subtype as target

2.9.5 (26-Mar-2018)

#1911: Allow serialization of `BigDecimal` as String, using
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonCreator.Mode;
import com.fasterxml.jackson.core.JsonLocation;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.fasterxml.jackson.databind.deser.impl.CreatorCandidate;
import com.fasterxml.jackson.databind.deser.impl.CreatorCollector;
import com.fasterxml.jackson.databind.deser.impl.JavaUtilCollectionsDeserializers;
import com.fasterxml.jackson.databind.deser.std.*;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.fasterxml.jackson.databind.ext.OptionalHandlerFactory;
import com.fasterxml.jackson.databind.introspect.*;
import com.fasterxml.jackson.databind.jsontype.NamedType;
Expand Down Expand Up @@ -1554,9 +1556,8 @@ public TypeDeserializer findTypeDeserializer(DeserializationConfig config,
AnnotationIntrospector ai = config.getAnnotationIntrospector();
TypeResolverBuilder<?> b = ai.findTypeResolver(config, ac, baseType);

/* Ok: if there is no explicit type info handler, we may want to
* use a default. If so, config object knows what to use.
*/
// Ok: if there is no explicit type info handler, we may want to
// use a default. If so, config object knows what to use.
Collection<NamedType> subtypes = null;
if (b == null) {
b = config.getDefaultTyper(baseType);
Expand All @@ -1574,7 +1575,16 @@ public TypeDeserializer findTypeDeserializer(DeserializationConfig config,
b = b.defaultImpl(defaultType.getRawClass());
}
}
return b.buildTypeDeserializer(config, baseType, subtypes);
// 05-Apt-2018, tatu: Since we get non-mapping exception due to various limitations,
// map to better type here
try {
return b.buildTypeDeserializer(config, baseType, subtypes);
} catch (IllegalArgumentException e0) {
InvalidDefinitionException e = InvalidDefinitionException.from((JsonParser) null,
e0.getMessage(), baseType);
e.initCause(e0);
throw e;
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ public abstract JsonDeserializer<?> createMapLikeDeserializer(DeserializationCon
public abstract KeyDeserializer createKeyDeserializer(DeserializationContext ctxt,
JavaType type)
throws JsonMappingException;

/**
* Method called to find and create a type information deserializer for given base type,
* if one is needed. If not needed (no polymorphic handling configured for type),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import com.fasterxml.jackson.databind.annotation.NoClass;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.jsontype.*;
import com.fasterxml.jackson.databind.util.ClassUtil;

/**
* Default {@link TypeResolverBuilder} implementation.
Expand Down Expand Up @@ -143,13 +142,17 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
defaultImpl = config.getTypeFactory()
.constructSpecializedType(baseType, _defaultImpl);
} else {
// 05-Apr-2018, tatu: [databind#1861] Not sure what would be the best way
// to handle, but for 2.9, let's consider case of "sibling" defaultImpl...
// ... Ugh. Not declared to throw `JsonMappingException`, so...
// 05-Apr-2018, tatu: As [databind#1565] and [databind#1861] need to allow
// some cases of seemingly incompatible `defaultImpl`. Easiest to just clear
// the setting.

/*
throw new IllegalArgumentException(
String.format("Invalid \"defaultImpl\" (%s): not a subtype of basetype (%s)",
ClassUtil.nameOf(_defaultImpl), ClassUtil.nameOf(baseType.getRawClass()))
);
*/
defaultImpl = null;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.fasterxml.jackson.databind.jsontype;

import com.fasterxml.jackson.annotation.*;

import com.fasterxml.jackson.databind.*;

public class TestPolymorphicWithDefaultImpl1565 extends BaseMapTest
{
// [databind#1565]
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY,
property="typeInfo", defaultImpl = CBaseClass1565.class)
@JsonSubTypes({
@JsonSubTypes.Type(CDerived1565.class)
})
public static interface CTestInterface1565
{
public String getName();
public void setName(String name);
public String getTypeInfo();
}

static class CBaseClass1565 implements CTestInterface1565
{
private String mName;

@Override
public String getName() {
return(mName);
}

@Override
public void setName(String name) {
mName = name;
}

@Override
public String getTypeInfo() {
return "base";
}
}

@JsonTypeName("derived")
static class CDerived1565 extends CBaseClass1565
{
public String description;

@Override
public String getTypeInfo() {
return "derived";
}
}

// [databind#1861]
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = DefaultImpl1861.class)
@JsonSubTypes({
@JsonSubTypes.Type(name = "a", value = Impl1861A.class)
})
static abstract class Bean1861 {
public String base;
}

static class DefaultImpl1861 extends Bean1861 {
public int id;
}

static class Impl1861A extends Bean1861 {
public int valueA;
}

/*
/**********************************************************************
/* Test methods
/**********************************************************************
*/

private final ObjectMapper MAPPER = new ObjectMapper();

// [databind#1565]
public void testIncompatibleDefaultImpl1565() throws Exception
{
String value = "{\"typeInfo\": \"derived\", \"name\": \"John\", \"description\": \"Owner\"}";
CDerived1565 result = MAPPER.readValue(value, CDerived1565.class);
assertNotNull(result);
}

// [databind#1861]
public void testWithIncompatibleTargetType1861() throws Exception
{
// Should allow deserialization even if `defaultImpl` incompatible
Impl1861A result = MAPPER.readValue(aposToQuotes("{'type':'a','base':'foo','valueA':3}"),
Impl1861A.class);
assertNotNull(result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.fasterxml.jackson.failing;

import java.util.*;

import com.fasterxml.jackson.databind.*;

@SuppressWarnings("serial")
public class SubTypeResolution1964Test extends BaseMapTest
{
static class AccessModel {
private Map<Object, Collection<String>> repositoryPrivileges;

public AccessModel() {
repositoryPrivileges = new HashMap<>();
}

public Map<Object, Collection<String>> getRepositoryPrivileges() {
return repositoryPrivileges;
}

public void setRepositoryPrivileges(Map<Object, Collection<String>> repositoryPrivileges) {
this.repositoryPrivileges = repositoryPrivileges;
}
}

static class CustomMap<T> extends LinkedHashMap<Object, T> { }

public void testTypeCompatibility1964() throws Exception
{
Map<Object, Collection<String>> repoPrivilegesMap = new CustomMap<>();
String key = "/storages/storage0/releases";
Collection<String> values = new HashSet<>();
values.add("ARTIFACTS_RESOLVE");
repoPrivilegesMap.put(key, values);

AccessModel accessModel = new AccessModel();
accessModel.setRepositoryPrivileges(repoPrivilegesMap);

ObjectMapper mapper = new ObjectMapper();
String jsonStr = mapper.writeValueAsString(accessModel);
assertNotNull(jsonStr);
}
}

0 comments on commit 09e5ba1

Please sign in to comment.