From 044419cd6c387e336b4a65b3cf495e54c299edd7 Mon Sep 17 00:00:00 2001 From: Tatu Date: Fri, 14 Feb 2014 13:12:58 -0800 Subject: [PATCH] Fixed #406 --- release-notes/VERSION | 2 + .../annotation/JsonTypeIdResolver.java | 5 +- .../databind/annotation/JsonTypeResolver.java | 5 +- .../jsontype/TestCustomTypeIdResolver.java | 96 ++++++++++++++----- 4 files changed, 82 insertions(+), 26 deletions(-) diff --git a/release-notes/VERSION b/release-notes/VERSION index 709200082e..81eece8554 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION @@ -8,6 +8,8 @@ Version: 2.3.2 (xx-xxx-2014) (reported by Chris P, cpilsworth@github) #398: Should deserialize empty (not null) URI from empty String (reported by pgieser@github) +#406: @JsonTypeIdResolver not working with external type ids + (repoted by Martin T) - Added `BeanSerializerBase._serializeObjectId()` needed by modules that override standard BeanSerializer; specifically, XML module. diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeIdResolver.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeIdResolver.java index c8d0f4f37f..8aa41cb522 100644 --- a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeIdResolver.java +++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeIdResolver.java @@ -18,8 +18,11 @@ * for converting between java types and type id included in JSON content. * In simplest cases this can be a simple class with static mapping between * type names and matching classes. + *

+ * NOTE: since 2.4, applicable to properties as well (should have been long time + * ago, but problem only found then) */ -@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE}) +@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @JacksonAnnotation public @interface JsonTypeIdResolver diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeResolver.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeResolver.java index 53156d29b5..b46e0aa715 100644 --- a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeResolver.java +++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeResolver.java @@ -9,8 +9,11 @@ * used for handling serialization and deserialization of type information, * needed for handling of polymorphic types (or sometimes just for linking * abstract types to concrete types) + *

+ * NOTE: since 2.4, applicable to properties as well (should have been long time + * ago, but problem only found then) */ -@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE}) +@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @com.fasterxml.jackson.annotation.JacksonAnnotation public @interface JsonTypeResolver diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestCustomTypeIdResolver.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestCustomTypeIdResolver.java index 0a4caddfe9..98b3d91ac4 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestCustomTypeIdResolver.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestCustomTypeIdResolver.java @@ -17,28 +17,66 @@ public class TestCustomTypeIdResolver extends BaseMapTest { @JsonTypeInfo(use=Id.CUSTOM, include=As.WRAPPER_OBJECT) @JsonTypeIdResolver(CustomResolver.class) - static class CustomBean { + static abstract class CustomBean { } + + static class CustomBeanImpl extends CustomBean { public int x; - public CustomBean() { } - public CustomBean(int x) { this.x = x; } + public CustomBeanImpl() { } + public CustomBeanImpl(int x) { this.x = x; } } - - static class CustomResolver implements TypeIdResolver - { + + static class ExtBeanWrapper { + @JsonTypeInfo(use=Id.CUSTOM, include=As.EXTERNAL_PROPERTY, property="type") + @JsonTypeIdResolver(ExtResolver.class) + public ExtBean value; + } + + static class CustomResolver extends CustomResolverBase { + // yes, static: just for test purposes, not real use static List initTypes; - public CustomResolver() { } - - @Override - public Id getMechanism() { - return Id.CUSTOM; + public CustomResolver() { + super(CustomBean.class, CustomBeanImpl.class); } @Override - public String idFromValue(Object value) - { - if (value.getClass() == CustomBean.class) { + public void init(JavaType baseType) { + if (initTypes != null) { + initTypes.add(baseType); + } + } + } + + static abstract class ExtBean { } + + static class ExtBeanImpl extends ExtBean { + public int y; + + public ExtBeanImpl() { } + public ExtBeanImpl(int y) { this.y = y; } + } + + static class ExtResolver extends CustomResolverBase { + public ExtResolver() { + super(ExtBean.class, ExtBeanImpl.class); + } + } + + static class CustomResolverBase implements TypeIdResolver + { + protected final Class superType; + protected final Class subType; + + public CustomResolverBase(Class baseType, Class implType) { + superType = baseType; + subType = implType; + } + + @Override public Id getMechanism() { return Id.CUSTOM; } + + @Override public String idFromValue(Object value) { + if (superType.isAssignableFrom(value.getClass())) { return "*"; } return "unknown"; @@ -50,17 +88,13 @@ public String idFromValueAndType(Object value, Class type) { } @Override - public void init(JavaType baseType) { - if (initTypes != null) { - initTypes.add(baseType); - } - } + public void init(JavaType baseType) { } @Override public JavaType typeFromId(String id) { if ("*".equals(id)) { - return TypeFactory.defaultInstance().constructType(CustomBean.class); + return TypeFactory.defaultInstance().constructType(subType); } return null; } @@ -77,24 +111,38 @@ public String idFromBaseType() { /********************************************************** */ + private final ObjectMapper MAPPER = objectMapper(); + // for [JACKSON-359] public void testCustomTypeIdResolver() throws Exception { - ObjectMapper m = new ObjectMapper(); List types = new ArrayList(); CustomResolver.initTypes = types; - String json = m.writeValueAsString(new CustomBean[] { new CustomBean(28) }); + String json = MAPPER.writeValueAsString(new CustomBean[] { new CustomBeanImpl(28) }); assertEquals("[{\"*\":{\"x\":28}}]", json); assertEquals(1, types.size()); assertEquals(CustomBean.class, types.get(0).getRawClass()); types = new ArrayList(); CustomResolver.initTypes = types; - CustomBean[] result = m.readValue(json, CustomBean[].class); + CustomBean[] result = MAPPER.readValue(json, CustomBean[].class); assertNotNull(result); assertEquals(1, result.length); - assertEquals(28, result[0].x); + assertEquals(28, ((CustomBeanImpl) result[0]).x); assertEquals(1, types.size()); assertEquals(CustomBean.class, types.get(0).getRawClass()); } + + public void testCustomWithExternal() throws Exception + { + ExtBeanWrapper w = new ExtBeanWrapper(); + w.value = new ExtBeanImpl(12); + + String json = MAPPER.writeValueAsString(w); + + ExtBeanWrapper out = MAPPER.readValue(json, ExtBeanWrapper.class); + assertNotNull(out); + + assertEquals(12, ((ExtBeanImpl) out.value).y); + } }