Skip to content

Commit

Permalink
Fix #1194
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Apr 14, 2016
1 parent 24de8a2 commit f2bb0df
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 74 deletions.
2 changes: 2 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ Project: jackson-databind
------------------------------------------------------------------------

2.7.4 (not yet released)

#1178: `@JsonSerialize(contentAs=superType)` behavior disallowed in 2.7
#1189: Converter called twice results in ClassCastException
(reported by carrino@github)
#1191: Non-matching quotes used in error message for date parsing
#1194: Incorrect signature for generic type via `JavaType.getGenericSignature
#1198: Problem with `@JsonTypeInfo.As.EXTERNAL_PROPERTY`, `defaultImpl`, missing type id, NPE
- Improve handling of custom content (de)serializers for `AtomicReference`

2.7.3 (16-Mar-2016)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,15 +257,15 @@ protected final void _deserializeAndSet(JsonParser p, DeserializationContext ctx
TokenBuffer merged = new TokenBuffer(p, ctxt);
merged.writeStartArray();
merged.writeString(typeId);

merged.copyCurrentStructure(p2);
merged.writeEndArray();
// needs to point to START_OBJECT (or whatever first token is)
JsonParser mp = merged.asParser(p);
mp.nextToken();
_properties[index].getProperty().deserializeAndSet(mp, ctxt, bean);
}

/*
/**********************************************************
/* Helper classes
Expand All @@ -284,7 +284,7 @@ public void addExternal(SettableBeanProperty property, TypeDeserializer typeDese
_nameToPropertyIndex.put(property.getName(), index);
_nameToPropertyIndex.put(typeDeser.getPropertyName(), index);
}

public ExternalTypeHandler build() {
return new ExternalTypeHandler(_properties.toArray(new ExtTypedProperty[_properties.size()]),
_nameToPropertyIndex, null, null);
Expand All @@ -296,7 +296,7 @@ private final static class ExtTypedProperty
private final SettableBeanProperty _property;
private final TypeDeserializer _typeDeserializer;
private final String _typePropertyName;

public ExtTypedProperty(SettableBeanProperty property, TypeDeserializer typeDeser)
{
_property = property;
Expand All @@ -312,16 +312,21 @@ public boolean hasDefaultType() {
return _typeDeserializer.getDefaultImpl() != null;
}

/**
* Specialized called when we need to expose type id of `defaultImpl` when
* serializing: we may need to expose it for assignment to a property, or
* it may be requested as visible for some other reason.
*/
public String getDefaultTypeId() {
Class<?> defaultType = _typeDeserializer.getDefaultImpl();
if (defaultType == null) {
return null;
}
return _typeDeserializer.getTypeIdResolver().idFromValueAndType(null, defaultType);
}

public String getTypePropertyName() { return _typePropertyName; }

public SettableBeanProperty getProperty() {
return _property;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
package com.fasterxml.jackson.databind.jsontype.impl;

import java.util.*;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DatabindContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.jsontype.NamedType;

import java.util.Collection;
import java.util.HashMap;
import java.util.TreeSet;

public class TypeNameIdResolver extends TypeIdResolverBase
{
protected final MapperConfig<?> _config;

/**
* Mappings from class name to type id, used for serialization
*/
protected final HashMap<String, String> _typeToId;
protected final Map<String, String> _typeToId;

/**
* Mappings from type id to JavaType, used for deserialization
*/
protected final HashMap<String, JavaType> _idToType;
protected final Map<String, JavaType> _idToType;

protected TypeNameIdResolver(MapperConfig<?> config, JavaType baseType,
HashMap<String, String> typeToId, HashMap<String, JavaType> idToType)
Map<String, String> typeToId, Map<String, JavaType> idToType)
{
super(baseType, config.getTypeFactory());
_config = config;
Expand All @@ -39,14 +37,17 @@ public static TypeNameIdResolver construct(MapperConfig<?> config, JavaType base
{
// sanity check
if (forSer == forDeser) throw new IllegalArgumentException();
HashMap<String, String> typeToId = null;
HashMap<String, JavaType> idToType = null;
Map<String, String> typeToId = null;
Map<String, JavaType> idToType = null;

if (forSer) {
typeToId = new HashMap<String, String>();
}
if (forDeser) {
idToType = new HashMap<String, JavaType>();
// 14-Apr-2016, tatu: Apparently needed for special case of `defaultImpl`;
// see [databind#1198] for details.
typeToId = new TreeMap<String, String>();
}
if (subtypes != null) {
for (NamedType t : subtypes) {
Expand All @@ -59,10 +60,8 @@ public static TypeNameIdResolver construct(MapperConfig<?> config, JavaType base
typeToId.put(cls.getName(), id);
}
if (forDeser) {
/* 24-Feb-2011, tatu: [JACKSON-498] One more problem; sometimes
* we have same name for multiple types; if so, use most specific
* one.
*/
// One more problem; sometimes we have same name for multiple types;
// if so, use most specific
JavaType prev = idToType.get(id);
if (prev != null) { // Can only override if more specific
if (cls.isAssignableFrom(prev.getRawClass())) { // nope, more generic (or same)
Expand All @@ -87,12 +86,13 @@ public String idFromValue(Object value)

protected String idFromClass(Class<?> clazz)
{
if(clazz==null){
if (clazz == null) {
return null;
}
Class<?> cls = _typeFactory.constructType(clazz).getRawClass();
final String key = cls.getName();
String name;

synchronized (_typeToId) {
name = _typeToId.get(key);
if (name == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.fasterxml.jackson.databind.jsontype;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

import com.fasterxml.jackson.databind.*;

public class ExternalTypeId198Test extends BaseMapTest
{
public enum Attacks { KICK, PUNCH }

static class Character {
public String name;
public Attacks preferredAttack;

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, defaultImpl=Kick.class,
include=JsonTypeInfo.As.EXTERNAL_PROPERTY, property="preferredAttack")
@JsonSubTypes({
@JsonSubTypes.Type(value=Kick.class, name="KICK"),
@JsonSubTypes.Type(value=Punch.class, name="PUNCH")
})
public Attack attack;
}

public static abstract class Attack {
public String side;

@JsonCreator
public Attack(String side) {
this.side = side;
}
}

public static class Kick extends Attack {
@JsonCreator
public Kick(String side) {
super(side);
}
}

public static class Punch extends Attack {
@JsonCreator
public Punch(String side) {
super(side);
}
}

final ObjectMapper MAPPER = new ObjectMapper();

public void testFails() throws Exception {
String json = "{ \"name\": \"foo\", \"attack\":\"right\" } }";

Character character = MAPPER.readValue(json, Character.class);

assertNotNull(character);
assertNotNull(character.attack);
assertEquals("foo", character.name);
}

public void testWorks() throws Exception {
String json = "{ \"name\": \"foo\", \"preferredAttack\": \"KICK\", \"attack\":\"right\" } }";

Character character = MAPPER.readValue(json, Character.class);

assertNotNull(character);
assertNotNull(character.attack);
assertEquals("foo", character.name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public ExternalBeanWithCreator(@JsonProperty("foo") int f)
value = new ValueBean(f);
}
}

@JsonTypeName("vbean")
static class ValueBean {
public int value;
Expand Down Expand Up @@ -251,6 +251,36 @@ static class Payload928 {
public String something;
}

enum Type965 { BIG_DECIMAL }

static class Wrapper965 {
protected Type965 typeEnum;

protected Object value;

@JsonGetter("type")
String getTypeString() {
return typeEnum.name();
}

@JsonSetter("type")
void setTypeString(String type) {
this.typeEnum = Type965.valueOf(type);
}

@JsonGetter(value = "objectValue")
Object getValue() {
return value;
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
@JsonSubTypes({ @JsonSubTypes.Type(name = "BIG_DECIMAL", value = BigDecimal.class) })
@JsonSetter(value = "objectValue")
private void setValue(Object value) {
this.value = value;
}
}

/*
/**********************************************************
/* Unit tests, serialization
Expand Down Expand Up @@ -395,29 +425,7 @@ public void testIssue831() throws Exception
assertEquals("dog", result.petType);
}

// For [Issue#96]: should allow use of default impl, if property missing
/* 18-Jan-2013, tatu: Unfortunately this collides with [Issue#118], and I don't
* know what the best resolution is. For now at least
*/
/*
public void testWithDefaultAndMissing() throws Exception
{
ExternalBeanWithDefault input = new ExternalBeanWithDefault(13);
// baseline: include type, verify things work:
String fullJson = MAPPER.writeValueAsString(input);
ExternalBeanWithDefault output = MAPPER.readValue(fullJson, ExternalBeanWithDefault.class);
assertNotNull(output);
assertNotNull(output.bean);
// and then try without type info...
ExternalBeanWithDefault defaulted = MAPPER.readValue("{\"bean\":{\"value\":13}}",
ExternalBeanWithDefault.class);
assertNotNull(defaulted);
assertNotNull(defaulted.bean);
assertSame(ValueBean.class, defaulted.bean.getClass());
}
*/

// For [Issue#118]
// For [databind#118]
// Note: String works fine, since no type id will used; other scalar types have issues
public void testWithScalar118() throws Exception
{
Expand All @@ -431,7 +439,7 @@ public void testWithScalar118() throws Exception
assertTrue(result.value instanceof java.util.Date);
}

// For [Issue#118] using "natural" type(s)
// For [databind#118] using "natural" type(s)
public void testWithNaturalScalar118() throws Exception
{
ExternalTypeWithNonPOJO input = new ExternalTypeWithNonPOJO(Integer.valueOf(13));
Expand Down Expand Up @@ -511,35 +519,6 @@ public void testInverseExternalId928() throws Exception
assertEquals(Payload928.class, envelope2._payload.getClass());
}

enum Type965 { BIG_DECIMAL }

static class Wrapper965 {
protected Type965 typeEnum;

protected Object value;

@JsonGetter("type")
String getTypeString() {
return typeEnum.name();
}

@JsonSetter("type")
void setTypeString(String type) {
this.typeEnum = Type965.valueOf(type);
}

@JsonGetter(value = "objectValue")
Object getValue() {
return value;
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
@JsonSubTypes({ @JsonSubTypes.Type(name = "BIG_DECIMAL", value = BigDecimal.class) })
@JsonSetter(value = "objectValue")
private void setValue(Object value) {
this.value = value;
}
}
// for [databind#965]
public void testBigDecimal965() throws Exception
{
Expand Down
Loading

0 comments on commit f2bb0df

Please sign in to comment.