Skip to content

Commit

Permalink
Providing a work-around-ish solution to FasterXML#1565.
Browse files Browse the repository at this point in the history
- Avoids using a default implementation when the defaultImpl anntotation
  should not apply.
  • Loading branch information
slobo-showbie committed Jun 13, 2017
1 parent 4f4e08f commit 158026a
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 91 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package com.fasterxml.jackson.databind.jsontype.impl;

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

import com.fasterxml.jackson.annotation.JsonTypeInfo;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.NoClass;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.jsontype.*;

/**
Expand Down Expand Up @@ -125,8 +128,13 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
|| (_defaultImpl == NoClass.class)) {
defaultImpl = config.getTypeFactory().constructType(_defaultImpl);
} else {
defaultImpl = config.getTypeFactory()
.constructSpecializedType(baseType, _defaultImpl);
if (!baseType.getRawClass().isAssignableFrom(_defaultImpl) && isBaseActuallySpecializedType(config, baseType)) {
defaultImpl = null;
}
else {
defaultImpl = config.getTypeFactory()
.constructSpecializedType(baseType, _defaultImpl);
}
}
}

Expand All @@ -149,6 +157,36 @@ public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
throw new IllegalStateException("Do not know how to construct standard type serializer for inclusion type: "+_includeAs);
}

private boolean isBaseActuallySpecializedType(DeserializationConfig config, JavaType baseType) {
// 12-Jun-2017, slobo: There is a chance that we're deserializing
// a specific class part of a polymorphic hierarchy. In this case
// baseType is actually a specific type, but the _defaultImpl comes
// from the real base type. For example:
//
// B @JsonTypeInfo(defaultImpl=S2)
// /--+--\
// S1 S2
// baseType = S1
// _defaultImpl = S2
//
// To detect this scenario we'll check whether _defaultImpl and
// baseType share a common superclass or super superclass, etc.
JavaType defaultType = config.getTypeFactory().constructType(_defaultImpl);
for (JavaType current = defaultType; current != null; current = current.getSuperClass())
{
AnnotatedClass annotatedClass = AnnotatedClass.construct(current, config);
if (annotatedClass.hasAnnotation(JsonTypeInfo.class)) {
if (annotatedClass.getRawType().isAssignableFrom(baseType.getRawClass())) {
return true;
}
}
else {
break; // ignore any classes above the JsonTypeInfo annotation
}
};
return false;
}

/*
/**********************************************************
/* Construction, configuration
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package com.fasterxml.jackson.databind.jsontype;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;

import java.io.IOException;

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
Expand Down Expand Up @@ -362,4 +368,81 @@ public void testIssue1125WithDefault() throws Exception
assertEquals(5, impl.b);
assertEquals(9, impl.def);
}


// [databind#1565]: defaultImpl and deserializing a concrete subclass

public void testDeserializeScenario1ToDefaultImpl() throws JsonProcessingException, IOException {
ObjectMapper om = objectMapper();
om.readerFor(Scenario1.Sub2.class).readValue("{\"type\":\"sub2\"}"); // no exception should happen
}

public void testDeserializeScenario3ToDefaultImpl() throws JsonProcessingException, IOException {
ObjectMapper om = objectMapper();
om.readerFor(Scenario3.Sub2.class).readValue("{\"type\":\"sub2\"}"); // no exception should happen
}

public void testDeserializeScenario4ToDefaultImpl() throws JsonProcessingException, IOException {
ObjectMapper om = objectMapper();
om.readerFor(Scenario4.Sub2.class).readValue("{\"type\":\"sub2\"}"); // no exception should happen
}

public void testDeserializeToUnrelatedDefaultImpl() throws JsonProcessingException, IOException {
try {
ObjectMapper om = objectMapper();
om.readerFor(Scenario2.BaseWithUnrelated.class).readValue("{}");
fail("JsonProcessingException was not thrown.");
}
catch (IllegalArgumentException e) { // should this throw another type of exception?
}
}

/**
* A base class with a subs and one of the is default.
*/
static class Scenario1 {
@JsonTypeInfo(include=As.PROPERTY, property="type", use=Id.NAME, defaultImpl=Sub1.class)
@JsonSubTypes({@Type(name="sub1", value=Sub1.class), @Type(name="sub2", value=Sub2.class)})
static abstract class Base {}

static class Sub1 extends Base {}

static class Sub2 extends Base {}
}

/**
* A base class with an unrelated default class. This is incorrect and should throw errors.
*/
static class Scenario2 {
@JsonTypeInfo(include=As.PROPERTY, property="type", use=Id.NAME, defaultImpl=Unrelated.class)
static abstract class BaseWithUnrelated {}

static class Unrelated {}
}

/**
* Multiple levels of inheritance. 2 Base classes 3 Subs. Annotations on the higher base class.
*/
static class Scenario3 {
@JsonTypeInfo(include=As.PROPERTY, property="type", use=Id.NAME, defaultImpl=Sub1.class)
@JsonSubTypes({@Type(name="sub1", value=Sub1.class), @Type(name="sub2", value=Sub2.class), @Type(name="sub3", value=Sub3.class)})
static abstract class BaseHigh {}
static abstract class BaseLow extends BaseHigh {}
static class Sub1 extends BaseLow {}
static class Sub2 extends BaseLow {}
static class Sub3 extends BaseHigh {}
}

/**
* Multiple levels of inheritance. 2 Base classes 3 Subs. Annotations on the lower base class
*/
static class Scenario4 {
static abstract class BaseHigh {}
@JsonTypeInfo(include=As.PROPERTY, property="type", use=Id.NAME, defaultImpl=Sub1.class)
@JsonSubTypes({@Type(name="sub1", value=Sub1.class), @Type(name="sub2", value=Sub2.class)})
static abstract class BaseLow extends BaseHigh {}
static class Sub1 extends BaseLow {}
static class Sub2 extends BaseLow {}
static class Sub3 extends BaseHigh {}
}
}

This file was deleted.

0 comments on commit 158026a

Please sign in to comment.