From edcec24754991bcb08827b95f83bd1f37b5437c7 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 26 Nov 2019 15:29:17 -0800 Subject: [PATCH] Fix #2553 --- release-notes/VERSION-2.x | 2 + .../jackson/databind/type/TypeFactory.java | 13 +++-- .../databind/deser/TestValueAnnotations.java | 58 ++++++++++++------- .../databind/type/TestTypeFactory.java | 1 + 4 files changed, 47 insertions(+), 27 deletions(-) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 0470e2e736..5270d29b8c 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -8,6 +8,8 @@ Project: jackson-databind #2544: java.lang.NoClassDefFoundError Thrown for compact profile1 (reported by Jon A) +#2553: JsonDeserialize(contentAs=...) broken with raw collections + (reported by cpopp@github) 2.10.1 (09-Nov-2019) diff --git a/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java b/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java index 00d9e5df8a..c6a3e77888 100644 --- a/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java +++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java @@ -381,12 +381,7 @@ public JavaType constructSpecializedType(JavaType baseType, Class subclass) } // A few special cases where we can simplify handling: - // (1) Original target type has no generics -- just resolve subtype - if (baseType.getBindings().isEmpty()) { - newType = _fromClass(null, subclass, EMPTY_BINDINGS); - break; - } - // (2) A small set of "well-known" List/Map subtypes where can take a short-cut + // (1) A small set of "well-known" List/Map subtypes where can take a short-cut if (baseType.isContainerType()) { if (baseType.isMapLikeType()) { if ((subclass == HashMap.class) @@ -413,6 +408,12 @@ public JavaType constructSpecializedType(JavaType baseType, Class subclass) } } } + // (2) Original target type has no generics -- just resolve subtype + if (baseType.getBindings().isEmpty()) { + newType = _fromClass(null, subclass, EMPTY_BINDINGS); + break; + } + // (3) Sub-class does not take type parameters -- just resolve subtype int typeParamCount = subclass.getTypeParameters().length; if (typeParamCount == 0) { diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestValueAnnotations.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestValueAnnotations.java index e14ad18c27..3f016fdbef 100644 --- a/src/test/java/com/fasterxml/jackson/databind/deser/TestValueAnnotations.java +++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestValueAnnotations.java @@ -190,6 +190,17 @@ public void setList(List l) { } } + // for [databind#2553] + @SuppressWarnings("rawtypes") + static class List2553 { + @JsonDeserialize(contentAs = Item2553.class) + public List items; + } + + static class Item2553 { + public String name; + } + final static class InvalidContentClass { /* Such annotation not allowed, since it makes no sense; @@ -230,10 +241,11 @@ public void setMap(Map m) /********************************************************** */ + private final ObjectMapper MAPPER = newJsonMapper(); + public void testOverrideClassValid() throws Exception { - ObjectMapper m = new ObjectMapper(); - CollectionHolder result = m.readValue + CollectionHolder result = MAPPER.readValue ("{ \"strings\" : [ \"test\" ] }", CollectionHolder.class); Collection strs = result._strings; @@ -244,9 +256,8 @@ public void testOverrideClassValid() throws Exception public void testOverrideMapValid() throws Exception { - ObjectMapper m = new ObjectMapper(); // note: expecting conversion from number to String, as well - MapHolder result = m.readValue + MapHolder result = MAPPER.readValue ("{ \"strings\" : { \"a\" : 3 } }", MapHolder.class); Map strs = result._data; @@ -258,8 +269,7 @@ public void testOverrideMapValid() throws Exception public void testOverrideArrayClass() throws Exception { - ObjectMapper m = new ObjectMapper(); - ArrayHolder result = m.readValue + ArrayHolder result = MAPPER.readValue ("{ \"strings\" : [ \"test\" ] }", ArrayHolder.class); String[] strs = result._strings; @@ -272,7 +282,7 @@ public void testOverrideClassInvalid() throws Exception { // should fail due to incompatible Annotation try { - BrokenCollectionHolder result = new ObjectMapper().readValue + BrokenCollectionHolder result = MAPPER.readValue ("{ \"strings\" : [ ] }", BrokenCollectionHolder.class); fail("Expected a failure, but got results: "+result); } catch (JsonMappingException jme) { @@ -288,21 +298,21 @@ public void testOverrideClassInvalid() throws Exception public void testRootInterfaceAs() throws Exception { - RootInterface value = new ObjectMapper().readValue("{\"a\":\"abc\" }", RootInterface.class); + RootInterface value = MAPPER.readValue("{\"a\":\"abc\" }", RootInterface.class); assertTrue(value instanceof RootInterfaceImpl); assertEquals("abc", value.getA()); } public void testRootInterfaceUsing() throws Exception { - RootString value = new ObjectMapper().readValue("\"xxx\"", RootString.class); + RootString value = MAPPER.readValue("\"xxx\"", RootString.class); assertTrue(value instanceof RootString); assertEquals("xxx", value.contents()); } public void testRootListAs() throws Exception { - RootMap value = new ObjectMapper().readValue("{\"a\":\"b\"}", RootMap.class); + RootMap value = MAPPER.readValue("{\"a\":\"b\"}", RootMap.class); assertEquals(1, value.size()); Object v2 = value.get("a"); assertEquals(RootStringImpl.class, v2.getClass()); @@ -311,7 +321,7 @@ public void testRootListAs() throws Exception public void testRootMapAs() throws Exception { - RootList value = new ObjectMapper().readValue("[ \"c\" ]", RootList.class); + RootList value = MAPPER.readValue("[ \"c\" ]", RootList.class); assertEquals(1, value.size()); Object v2 = value.get(0); assertEquals(RootStringImpl.class, v2.getClass()); @@ -327,8 +337,7 @@ public void testRootMapAs() throws Exception @SuppressWarnings("unchecked") public void testOverrideKeyClassValid() throws Exception { - ObjectMapper m = new ObjectMapper(); - MapKeyHolder result = m.readValue("{ \"map\" : { \"xxx\" : \"yyy\" } }", MapKeyHolder.class); + MapKeyHolder result = MAPPER.readValue("{ \"map\" : { \"xxx\" : \"yyy\" } }", MapKeyHolder.class); Map map = (Map)(Map)result._map; assertEquals(1, map.size()); Map.Entry en = map.entrySet().iterator().next(); @@ -343,7 +352,7 @@ public void testOverrideKeyClassInvalid() throws Exception { // should fail due to incompatible Annotation try { - BrokenMapKeyHolder result = new ObjectMapper().readValue + BrokenMapKeyHolder result = MAPPER.readValue ("{ \"123\" : \"xxx\" }", BrokenMapKeyHolder.class); fail("Expected a failure, but got results: "+result); } catch (JsonMappingException jme) { @@ -358,10 +367,9 @@ public void testOverrideKeyClassInvalid() throws Exception */ @SuppressWarnings("unchecked") - public void testOverrideContentClassValid() throws Exception + public void testOverrideContentClassValid() throws Exception { - ObjectMapper m = new ObjectMapper(); - ListContentHolder result = m.readValue("{ \"list\" : [ \"abc\" ] }", ListContentHolder.class); + ListContentHolder result = MAPPER.readValue("{ \"list\" : [ \"abc\" ] }", ListContentHolder.class); List list = (List)result._list; assertEquals(1, list.size()); Object value = list.get(0); @@ -371,8 +379,7 @@ public void testOverrideContentClassValid() throws Exception public void testOverrideArrayContents() throws Exception { - ObjectMapper m = new ObjectMapper(); - ArrayContentHolder result = m.readValue("{ \"data\" : [ 1, 2, 3 ] }", + ArrayContentHolder result = MAPPER.readValue("{ \"data\" : [ 1, 2, 3 ] }", ArrayContentHolder.class); Object[] data = result._data; assertEquals(3, data.length); @@ -384,8 +391,7 @@ public void testOverrideArrayContents() throws Exception public void testOverrideMapContents() throws Exception { - ObjectMapper m = new ObjectMapper(); - MapContentHolder result = m.readValue("{ \"map\" : { \"a\" : 9 } }", + MapContentHolder result = MAPPER.readValue("{ \"map\" : { \"a\" : 9 } }", MapContentHolder.class); Map map = result._map; assertEquals(1, map.size()); @@ -393,4 +399,14 @@ public void testOverrideMapContents() throws Exception assertEquals(Integer.class, ob.getClass()); assertEquals(Integer.valueOf(9), ob); } + + // [databind#2553] + public void testRawListTypeContentAs() throws Exception + { + List2553 list = MAPPER.readValue("{\"items\": [{\"name\":\"item1\"}]}", List2553.class); + assertEquals(1, list.items.size()); + Object value = list.items.get(0); + assertEquals(Item2553.class, value.getClass()); + assertEquals("item1", ((Item2553) value).name); + } } diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java index 60bcee8dd0..aba0a28307 100644 --- a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java +++ b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java @@ -282,6 +282,7 @@ public void testCollections() JavaType t = tf.constructType(ArrayList.class); assertEquals(CollectionType.class, t.getClass()); assertSame(ArrayList.class, t.getRawClass()); + assertSame(Object.class, ((CollectionType) t).getContentType().getRawClass()); // And then the proper way t = tf.constructType(new TypeReference>() { });