From 0303f98a2d6afb35e8f1261eee44eed11c11aaa9 Mon Sep 17 00:00:00 2001 From: ancane Date: Sun, 29 May 2022 13:23:07 +0300 Subject: [PATCH] check for repeated logical subtype names #3500 --- .../JacksonAnnotationIntrospector.java | 33 +++++++++++++++++ .../jsontype/TestMultipleTypeNames.java | 36 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java index c8bebb5199a..412cf68650c 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java @@ -621,14 +621,47 @@ public List findSubtypes(Annotated a) JsonSubTypes t = _findAnnotation(a, JsonSubTypes.class); if (t == null) return null; JsonSubTypes.Type[] types = t.value(); + + if (t.allowRepeatedNames()) { + ArrayList result = new ArrayList(types.length); + for (JsonSubTypes.Type type : types) { + result.add(new NamedType(type.value(), type.name())); + // [databind#2761]: alternative set of names to use + for (String name : type.names()) { + result.add(new NamedType(type.value(), name)); + } + } + return result; + } else { + return findSubtypesCheckRepeatedNames(a.getName(), types); + } + } + + private List findSubtypesCheckRepeatedNames(String annotatedTypeName, JsonSubTypes.Type[] types) + { ArrayList result = new ArrayList(types.length); + Set seenNames = new HashSet<>(); for (JsonSubTypes.Type type : types) { result.add(new NamedType(type.value(), type.name())); + + if (!type.name().isEmpty() && seenNames.contains(type.name())) { + throw new IllegalArgumentException("Annotated type [" + annotatedTypeName + "] got repeated subtype name [" + type.name() + "]"); + } else { + seenNames.add(type.name()); + } + // [databind#2761]: alternative set of names to use for (String name : type.names()) { result.add(new NamedType(type.value(), name)); + + if (!name.isEmpty() && seenNames.contains(name)) { + throw new IllegalArgumentException("Annotated type [" + annotatedTypeName + "] got repeated subtype name [" + name + "]"); + } else { + seenNames.add(name); + } } } + return result; } diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestMultipleTypeNames.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestMultipleTypeNames.java index b3c62224b9c..bca2203110b 100644 --- a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestMultipleTypeNames.java +++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestMultipleTypeNames.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.BaseMapTest; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.InvalidDefinitionException; import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException; import java.util.List; @@ -72,6 +73,28 @@ static class BaseForNameAndNamesTest { public MultiTypeName getData() { return data; } } + static class WrapperForNotUniqueNamesTest { + List base; + public List getBase() { return base; } + } + + static class BaseForNotUniqueNamesTest { + private String type; + public String getType() { return type; } + + @JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXTERNAL_PROPERTY, + property = "type" + ) + @JsonSubTypes(value = { + @JsonSubTypes.Type(value = A.class, name = "a"), + @JsonSubTypes.Type(value = B.class, names = {"b","a"}), + }, allowRepeatedNames = false) + MultiTypeName data; + public MultiTypeName getData() { return data; } + } + /* /********************************************************** /* Test methods @@ -133,4 +156,17 @@ public void testNameAndNames() throws Exception verifyException(e, "Unrecognized field \"data\""); } } + + public void testNotUniqueNameAndNames() throws Exception + { + String json = "{\"base\": [{\"type\":\"a\", \"data\": {\"x\": 5}}, {\"type\":\"b\", \"data\": {\"y\": 3.1}}, {\"type\":\"c\", \"data\": {\"y\": 33.8}}]}"; + + try { + MAPPER.readValue(json, WrapperForNotUniqueNamesTest.class); + fail("This serialisation should fail because of repeated subtype name"); + } catch (InvalidDefinitionException e) { + verifyException(e, "Annotated type [data] got repeated subtype name [a]"); + } + } + }