Skip to content

Commit

Permalink
Minor tweaks to fix for #1983, update release notes
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Feb 17, 2020
1 parent e5d22e2 commit d354028
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 52 deletions.
5 changes: 5 additions & 0 deletions release-notes/CREDITS-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -1083,3 +1083,8 @@ Oleksii Khomchenko (gagoman@github)
* Reported, contributed fix for #2592: `ObjectMapper.setSerializationInclusion()` is
ignored for `JsonAnyGetter`
(2.11.0)
Oleksandr Poslavskyi (alevskyi@github)
* Contributed fix for #1983: Polymorphic deserialization should handle case-insensitive Type Id
property name if `MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES` is enabled
(2.11.0)
3 changes: 3 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Project: jackson-databind

#953: i-I case conversion problem in Turkish locale with case-insensitive deserialization
(reported by Máté R)
#1983: Polymorphic deserialization should handle case-insensitive Type Id property name
if `MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES` is enabled
(reported by soundvibe@github, fix contributed by Oleksandr P)
#2049: TreeTraversingParser and UTF8StreamJsonParser create contexts differently
(reported by Antonio P)
#2352: Support use of `@JsonAlias` for enum values
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public AsPropertyTypeDeserializer(JavaType bt, TypeIdResolver idRes,
{
this(bt, idRes, typePropertyName, typeIdVisible, defaultImpl, As.PROPERTY);
}

/**
* @since 2.8
*/
Expand Down Expand Up @@ -89,15 +89,13 @@ public Object deserializeTypedFromObject(JsonParser p, DeserializationContext ct
}
// Ok, let's try to find the property. But first, need token buffer...
TokenBuffer tb = null;

boolean ignoreCase = ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);

for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) {
String name = p.getCurrentName();
if (ignoreCase) {
name = name.toLowerCase();
}
final String name = p.getCurrentName();
p.nextToken(); // to point to the value
if (name.equals(_typePropertyName)) { // gotcha!
if (name.equals(_typePropertyName)
|| (ignoreCase && name.equalsIgnoreCase(_typePropertyName))) { // gotcha!
return _deserializeTypedForId(p, ctxt, tb);
}
if (tb == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public class TypeNameIdResolver extends TypeIdResolverBase
*/
protected final Map<String, JavaType> _idToType;

/**
* @since 2.11
*/
protected final boolean _caseInsensitive;

protected TypeNameIdResolver(MapperConfig<?> config, JavaType baseType,
ConcurrentHashMap<String, String> typeToId,
HashMap<String, JavaType> idToType)
Expand All @@ -38,6 +43,7 @@ protected TypeNameIdResolver(MapperConfig<?> config, JavaType baseType,
_config = config;
_typeToId = typeToId;
_idToType = idToType;
_caseInsensitive = config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES);
}

public static TypeNameIdResolver construct(MapperConfig<?> config, JavaType baseType,
Expand All @@ -61,6 +67,8 @@ public static TypeNameIdResolver construct(MapperConfig<?> config, JavaType base
// for a single value.
typeToId = new ConcurrentHashMap<>(4);
}
final boolean caseInsensitive = config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES);

if (subtypes != null) {
for (NamedType t : subtypes) {
// no name? Need to figure out default; for now, let's just
Expand All @@ -71,6 +79,10 @@ public static TypeNameIdResolver construct(MapperConfig<?> config, JavaType base
typeToId.put(cls.getName(), id);
}
if (forDeser) {
// [databind#1983]: for case-insensitive lookups must canonicalize:
if (caseInsensitive) {
id = id.toLowerCase();
}
// One more problem; sometimes we have same name for multiple types;
// if so, use most specific
JavaType prev = idToType.get(id);
Expand Down Expand Up @@ -139,7 +151,8 @@ public JavaType typeFromId(DatabindContext context, String id) {
}

protected JavaType _typeFromId(String id) {
if(_config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)) {
// [databind#1983]: for case-insensitive lookups must canonicalize:
if (_caseInsensitive) {
id = id.toLowerCase();
}
// Now: if no type is found, should we try to locate it by
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.fasterxml.jackson.databind.jsontype;

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.exc.InvalidTypeIdException;

// Tests wrt [databind#1983]
public class JsonTypeInfoCaseInsensitive1983Test extends BaseMapTest
{
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
property = "Operation")
@JsonSubTypes({
@JsonSubTypes.Type(value = Equal.class, name = "eq"),
@JsonSubTypes.Type(value = NotEqual.class, name = "notEq"),
})
static abstract class Filter {
}

static class Equal extends Filter { }

static class NotEqual extends Filter { }

// verify failures when exact matching required:
private final ObjectMapper MAPPER = newJsonMapper();

public void testReadMixedCaseSubclass() throws Exception
{
final String serialised = "{\"Operation\":\"NoTeQ\"}";

// first: mismatch with value unless case-sensitivity disabled:
try {
MAPPER.readValue(serialised, Filter.class);
fail("Should not pass");
} catch (InvalidTypeIdException e) {
verifyException(e, "Could not resolve type id 'NoTeQ'");
}

ObjectMapper mapper = jsonMapperBuilder()
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES)
.build();
// Type id ("value") mismatch, should work now:
Filter result = mapper.readValue(serialised, Filter.class);

assertEquals(NotEqual.class, result.getClass());
}

public void testReadMixedCasePropertyName() throws Exception
{
final String serialised = "{\"oPeRaTioN\":\"notEq\"}";
// first: mismatch with property name unless case-sensitivity disabled:
try {
MAPPER.readValue(serialised, Filter.class);
fail("Should not pass");
} catch (InvalidTypeIdException e) {
verifyException(e, "Missing type id when trying to resolve subtype");
}

ObjectMapper mapper = jsonMapperBuilder()
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
.build();
// Type property name mismatch (but value match); should work:
Filter result = mapper.readValue(serialised, Filter.class);

assertEquals(NotEqual.class, result.getClass());
}
}

This file was deleted.

0 comments on commit d354028

Please sign in to comment.