-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Add DeserializationFeature.FAIL_ON_SUBTYPE_CLASS_NOT_REGISTERED
#5027
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
Changes from 8 commits
9f09361
a460c2a
17edca8
9fa9678
7c92fa8
a1d812a
c81688f
eb299c0
03fc82a
f422ff6
8cb3fa1
46243d4
1f065e8
d682fe8
ca3ddfd
14135d8
4fbd122
9bcd018
a1a4054
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
|
||
import com.fasterxml.jackson.databind.*; | ||
import com.fasterxml.jackson.databind.cfg.MapperConfig; | ||
import com.fasterxml.jackson.databind.jsontype.NamedType; | ||
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator; | ||
import com.fasterxml.jackson.databind.type.TypeFactory; | ||
import com.fasterxml.jackson.databind.util.ClassUtil; | ||
|
@@ -26,6 +27,8 @@ public class ClassNameIdResolver | |
|
||
protected final PolymorphicTypeValidator _subTypeValidator; | ||
|
||
private Set<String> _allowedSubtypes; | ||
|
||
/** | ||
* @deprecated Since 2.10 use variant that takes {@link PolymorphicTypeValidator} | ||
*/ | ||
|
@@ -36,18 +39,48 @@ protected ClassNameIdResolver(JavaType baseType, TypeFactory typeFactory) { | |
|
||
/** | ||
* @since 2.10 | ||
* @deprecated Since 2.19 use variant that takes {@link Collection<NamedType>} | ||
*/ | ||
public ClassNameIdResolver(JavaType baseType, TypeFactory typeFactory, | ||
PolymorphicTypeValidator ptv) { | ||
this(baseType, typeFactory, null, ptv); | ||
} | ||
|
||
/** | ||
* @since 2.19 | ||
*/ | ||
public ClassNameIdResolver(JavaType baseType, TypeFactory typeFactory, | ||
Collection<NamedType> subtypes, PolymorphicTypeValidator ptv) { | ||
super(baseType, typeFactory); | ||
_subTypeValidator = ptv; | ||
if (subtypes != null) { | ||
for (NamedType t : subtypes) { | ||
final Class<?> cls = t.getType(); | ||
if (_allowedSubtypes == null) { | ||
_allowedSubtypes = new HashSet<>(); | ||
} | ||
_allowedSubtypes.add(cls.getName()); | ||
} | ||
} | ||
} | ||
|
||
public static ClassNameIdResolver construct(JavaType baseType, MapperConfig<?> config, | ||
PolymorphicTypeValidator ptv) { | ||
/** | ||
* @deprecated since 2.19 | ||
*/ | ||
public static ClassNameIdResolver construct(JavaType baseType, | ||
MapperConfig<?> config, PolymorphicTypeValidator ptv) { | ||
return new ClassNameIdResolver(baseType, config.getTypeFactory(), ptv); | ||
} | ||
|
||
/** | ||
* @since 2.19 | ||
*/ | ||
public static ClassNameIdResolver construct(JavaType baseType, MapperConfig<?> config, | ||
Collection<NamedType> subtypes, | ||
PolymorphicTypeValidator ptv) { | ||
return new ClassNameIdResolver(baseType, config.getTypeFactory(), subtypes, ptv); | ||
} | ||
|
||
@Override | ||
public JsonTypeInfo.Id getMechanism() { return JsonTypeInfo.Id.CLASS; } | ||
|
||
|
@@ -72,14 +105,21 @@ public JavaType typeFromId(DatabindContext context, String id) throws IOExceptio | |
|
||
protected JavaType _typeFromId(String id, DatabindContext ctxt) throws IOException | ||
{ | ||
// 24-Apr-2019, tatu: [databind#2195] validate as well as resolve: | ||
JavaType t = ctxt.resolveAndValidateSubType(_baseType, id, _subTypeValidator); | ||
if (t == null) { | ||
if (ctxt instanceof DeserializationContext) { | ||
// First: we may have problem handlers that can deal with it? | ||
return ((DeserializationContext) ctxt).handleUnknownTypeId(_baseType, id, this, "no such class found"); | ||
DeserializationContext deserializationContext = null; | ||
if (ctxt instanceof DeserializationContext) { | ||
deserializationContext = (DeserializationContext) ctxt; | ||
} | ||
if (_allowedSubtypes != null && deserializationContext != null | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Without There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess in the current impl, you can omit the JsonSubTypes annotation and then all subtypes are valid. Maybe I should change that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @cowtowncoder I changed the code to enforce the check if enabled even when JsonSubTyoes are not defined |
||
&& deserializationContext.isEnabled( | ||
DeserializationFeature.FAIL_ON_SUBTYPE_CLASS_NOT_REGISTERED)) { | ||
if (!_allowedSubtypes.contains(id)) { | ||
throw deserializationContext.invalidTypeIdException(_baseType, id, | ||
"DeserializationFeature.FAIL_ON_SUBTYPE_CLASS_NOT_REGISTERED is explicitly enabled and the input class is not registered using JsonSubTypes annotation."); | ||
} | ||
// ... meaning that we really should never get here. | ||
} | ||
final JavaType t = ctxt.resolveAndValidateSubType(_baseType, id, _subTypeValidator); | ||
if (t == null && deserializationContext != null) { | ||
return deserializationContext.handleUnknownTypeId(_baseType, id, this, "no such class found"); | ||
} | ||
return t; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package com.fasterxml.jackson.databind.deser; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's different package for polymorphic deser... let me see. |
||
|
||
import com.fasterxml.jackson.annotation.JsonSubTypes; | ||
import com.fasterxml.jackson.annotation.JsonTypeInfo; | ||
import com.fasterxml.jackson.databind.DeserializationFeature; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.databind.exc.InvalidTypeIdException; | ||
import com.fasterxml.jackson.databind.testutil.DatabindTestUtil; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
public class PolymorphicIdClassDeserTest extends DatabindTestUtil { | ||
|
||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) | ||
@JsonSubTypes({@JsonSubTypes.Type(value = FooClassImpl.class)}) | ||
cowtowncoder marked this conversation as resolved.
Show resolved
Hide resolved
|
||
static abstract class FooClass { } | ||
static class FooClassImpl extends FooClass { } | ||
static class FooClassImpl2 extends FooClass { } | ||
|
||
@JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS) | ||
@JsonSubTypes({@JsonSubTypes.Type(value = FooMinClassImpl.class)}) | ||
static abstract class FooMinClass { } | ||
static class FooMinClassImpl extends FooMinClass { } | ||
static class FooMinClassImpl2 extends FooMinClass { } | ||
|
||
/* | ||
/************************************************************ | ||
/* Unit tests, valid | ||
/************************************************************ | ||
*/ | ||
|
||
private final ObjectMapper MAPPER = jsonMapperBuilder() | ||
.enable(DeserializationFeature.FAIL_ON_SUBTYPE_CLASS_NOT_REGISTERED) | ||
.build(); | ||
|
||
@Test | ||
public void testDeserializationIdClass() throws Exception | ||
{ | ||
//trying to test if JsonSubTypes enforced | ||
final String foo1 = MAPPER.writeValueAsString(new FooClassImpl()); | ||
final String foo2 = MAPPER.writeValueAsString(new FooClassImpl2()); | ||
FooClass res1 = MAPPER.readValue(foo1, FooClass.class); | ||
assertTrue(res1 instanceof FooClassImpl); | ||
// next bit should in theory fail because FooClassImpl2 is not listed as a subtype | ||
assertThrows(InvalidTypeIdException.class, () -> MAPPER.readValue(foo2, FooClass.class)); | ||
} | ||
|
||
@Test | ||
public void testDeserializationIdMinimalClass() throws Exception | ||
{ | ||
//trying to test if JsonSubTypes enforced | ||
final String foo1 = MAPPER.writeValueAsString(new FooMinClassImpl()); | ||
final String foo2 = MAPPER.writeValueAsString(new FooMinClassImpl2()); | ||
FooMinClass res1 = MAPPER.readValue(foo1, FooMinClass.class); | ||
assertTrue(res1 instanceof FooMinClassImpl); | ||
// next bit should in theory fail because FooMinClassImpl2 is not listed as a subtype | ||
assertThrows(InvalidTypeIdException.class, () -> MAPPER.readValue(foo2, FooMinClass.class)); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll change code a bit to make this
final
.