Skip to content

Commit

Permalink
Fix #1998
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Apr 19, 2018
1 parent 0f7024e commit 10f2ce3
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 2 deletions.
3 changes: 3 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Project: jackson-databind

#1565: Deserialization failure with Polymorphism using JsonTypeInfo `defaultImpl`,
subtype as target
#1998: Removing "type" attribute with Mixin not taken in account if
using ObjectMapper.copy()
(reported by SBKila@github)

2.9.5 (26-Mar-2018)

Expand Down
20 changes: 20 additions & 0 deletions src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,26 @@ public BaseSettings(ClassIntrospector ci, AnnotationIntrospector ai,
_defaultBase64 = defaultBase64;
}

/**
* Turns out we are not necessarily 100% stateless, alas, since {@link ClassIntrospector}
* typically has a cache. So this method is needed for deep copy() of Mapper.
*
* @since 2.9.6
*/
public BaseSettings copy() {
return new BaseSettings(_classIntrospector.copy(),
_annotationIntrospector,
_propertyNamingStrategy,
_typeFactory,
_typeResolverBuilder,
_dateFormat,
_handlerInstantiator,
_locale,
_timeZone,
_defaultBase64);

}

/*
/**********************************************************
/* Factory methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,18 @@ protected MapperConfigBase(BaseSettings base,
}

/**
* Copy constructor usually called to make a copy for use by
* ObjectMapper that is copied.
*
* @since 2.8
*/
protected MapperConfigBase(MapperConfigBase<CFG,T> src,
SimpleMixInResolver mixins, RootNameLookup rootNames,
ConfigOverrides configOverrides)
{
super(src);
// 18-Apr-2018, tatu: [databind#1898] need to force copying of `ClassIntrospector`
// (to clear its cache) to avoid leakage
super(src, src._base.copy());
_mixIns = mixins;
_subtypeResolver = src._subtypeResolver;
_rootNames = rootNames;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,12 @@ public BasicClassIntrospector() {
// a small cache should go a long way here
_cachedFCA = new LRUMap<JavaType,BasicBeanDescription>(16, 64);
}


@Override
public ClassIntrospector copy() {
return new BasicClassIntrospector();
}

/*
/**********************************************************
/* Factory method impls
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ public interface MixInResolver

protected ClassIntrospector() { }

/**
* Method that may be needed when `copy()`ing `ObjectMapper` instances.
*
* @since 2.9.6
*/
public abstract ClassIntrospector copy();

/*
/**********************************************************
/* Public API: factory methods
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package com.fasterxml.jackson.databind.mixins;

import java.io.IOException;

import com.fasterxml.jackson.annotation.*;

import com.fasterxml.jackson.databind.*;

public class MapperMixinsCopy1998Test extends BaseMapTest
{
final static String FULLMODEL="{\"format\":\"1.0\",\"child\":{\"type\":\"CHILD_B\",\"name\":\"testB\"},\"notVisible\":\"should not be present\"}";
final static String EXPECTED="{\"format\":\"1.0\",\"child\":{\"name\":\"testB\"}}";

static class MyModelView { }

interface MixinConfig {
interface MyModelRoot {
@JsonView(MyModelView.class)
public String getFormat();

@JsonView(MyModelView.class)
public MyModelChildBase getChild();
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NONE, include = JsonTypeInfo.As.EXISTING_PROPERTY)
interface MyModelChildBase {
@JsonView(MyModelView.class)
public String getName();
}

}

@JsonPropertyOrder({ "format", "child" })
static class MyModelRoot {
@JsonProperty
private String format = "1.0";

public String getFormat() {
return format;
}
@JsonProperty
private MyModelChildBase child;

public MyModelChildBase getChild() {
return child;
}

public void setChild(MyModelChildBase child) {
this.child = child;
}

@JsonProperty
private String notVisible = "should not be present";

public String getNotVisible() {
return notVisible;
}
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = MyChildA.class, name = "CHILD_A"),
@JsonSubTypes.Type(value = MyChildB.class, name = "CHILD_B")
})
abstract static class MyModelChildBase {
@JsonProperty
private String name;

public String getName() {
return name;
}

@JsonIgnore
public void setName(String name) {
this.name = name;
}
}

static class MyChildA extends MyModelChildBase {
public MyChildA(String name) {
setName(name);
}
}

static class MyChildB extends MyModelChildBase {
public MyChildB(String name) {
setName(name);
}
}

public void testB_KO() throws Exception
{
final ObjectMapper DEFAULT = defaultMapper();
MyModelRoot myModelInstance = new MyModelRoot();
myModelInstance.setChild(new MyChildB("testB"));

ObjectMapper myObjectMapper = DEFAULT.copy();

String postResult = getString(myModelInstance, myObjectMapper);
assertEquals(FULLMODEL, postResult);
// System.out.println("postResult: "+postResult);

myObjectMapper = DEFAULT.copy();
// myObjectMapper = defaultMapper();
myObjectMapper.addMixIn(MyModelRoot.class, MixinConfig.MyModelRoot.class)
.addMixIn(MyModelChildBase.class, MixinConfig.MyModelChildBase.class)
.disable(MapperFeature.DEFAULT_VIEW_INCLUSION)
.setConfig(myObjectMapper.getSerializationConfig().withView(MyModelView.class));

String result = getString(myModelInstance, myObjectMapper);
System.out.println("result: "+result);
assertEquals(EXPECTED, result);

}

private String getString(MyModelRoot myModelInstance, ObjectMapper myObjectMapper) throws IOException {
return myObjectMapper.writerFor(MyModelRoot.class).writeValueAsString(myModelInstance);
}

private ObjectMapper defaultMapper()
{
return new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, false)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true)
;
}
}

0 comments on commit 10f2ce3

Please sign in to comment.