diff options
author | Tatu Saloranta <tatu.saloranta@iki.fi> | 2018-04-19 20:39:30 -0700 |
---|---|---|
committer | Tatu Saloranta <tatu.saloranta@iki.fi> | 2018-04-19 20:39:30 -0700 |
commit | 11808ccb068a75e1d2c9ae81e8c63f1fcbeb55f7 (patch) | |
tree | 607c45e9acaf7ec1c447587ecc1936d10830b661 | |
parent | 69f6f52da2204839e0826a1e79ce43781bbcf0d1 (diff) | |
download | jackson-databind-11808ccb068a75e1d2c9ae81e8c63f1fcbeb55f7.tar.gz |
Fixed #1990
6 files changed, 77 insertions, 34 deletions
diff --git a/release-notes/CREDITS-2.x b/release-notes/CREDITS-2.x index 27400427c..5792c751c 100644 --- a/release-notes/CREDITS-2.x +++ b/release-notes/CREDITS-2.x @@ -768,6 +768,10 @@ roeltje25@github infinite loop (2.9.5) +Freddy Boucher (freddyboucher@github) + * Reported #1990: MixIn `@JsonProperty` for `Object.hashCode()` is ignored + (2.9.6) + Ondrej Zizka (OndraZizk@github) * Reported #1999: "Duplicate property" issue should mention which class it complains about (2.9.6) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index da0a2cb69..465615d1d 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -11,6 +11,8 @@ Project: jackson-databind #1964: Failed to specialize `Map` type during serialization where key type incompatibility overidden via "raw" types (reported by ptirador@github) +#1990: MixIn `@JsonProperty` for `Object.hashCode()` is ignored + (reported by Freddy B) #1998: Removing "type" attribute with Mixin not taken in account if using ObjectMapper.copy() (reported by SBKila@github) diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodCollector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodCollector.java index 1e003c824..0341e3a2a 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodCollector.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodCollector.java @@ -49,37 +49,40 @@ public class AnnotatedMethodCollector type.getRawClass(), methods, mixin); } // Special case: mix-ins for Object.class? (to apply to ALL classes) - /* + boolean checkJavaLangObject = false; if (_mixInResolver != null) { Class<?> mixin = _mixInResolver.findMixInClassFor(Object.class); if (mixin != null) { - _addMethodMixIns(tc, mainType.getRawClass(), memberMethods, mixin, mixins); + _addMethodMixIns(tc, mainType.getRawClass(), methods, mixin); //, mixins); + checkJavaLangObject = true; } } // Any unmatched mix-ins? Most likely error cases (not matching any method); // but there is one possible real use case: exposing Object#hashCode // (alas, Object#getClass can NOT be exposed) - if (_intr != null) { - if (!mixins.isEmpty()) { - Iterator<AnnotatedMethod> it = mixins.iterator(); - while (it.hasNext()) { - AnnotatedMethod mixIn = it.next(); - try { - Method m = Object.class.getDeclaredMethod(mixIn.getName(), mixIn.getRawParameterTypes()); - if (m != null) { - // Since it's from java.lang.Object, no generics, no need for real type context: - AnnotatedMethod am = _constructMethod(tc, m); - _addMixOvers(mixIn.getAnnotated(), am, false); - memberMethods.add(am); - } - } catch (Exception e) { } + // Since we only know of that ONE case, optimize for it + if (checkJavaLangObject && (_intr != null) && !methods.isEmpty()) { + // Could use lookup but probably as fast or faster to traverse + for (Map.Entry<MemberKey,MethodBuilder> entry : methods.entrySet()) { + MemberKey k = entry.getKey(); + if (!"hashCode".equals(k.getName()) || (0 != k.argCount())) { + continue; } + try { + // And with that, we can generate it appropriately + Method m = Object.class.getDeclaredMethod(k.getName()); + if (m != null) { + MethodBuilder b = entry.getValue(); + b.annotations = collectDefaultAnnotations(b.annotations, + m.getDeclaredAnnotations()); + b.method = m; + } + } catch (Exception e) { } } } - */ - // And then let's + // And then let's create the lookup map if (methods.isEmpty()) { return new AnnotatedMethodMap(); } diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/CollectorBase.java b/src/main/java/com/fasterxml/jackson/databind/introspect/CollectorBase.java index c8608ae3b..e52dcb453 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/CollectorBase.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/CollectorBase.java @@ -68,7 +68,8 @@ class CollectorBase // // // Defaulting ("mix under") // Variant that only adds annotations that are missing - protected final AnnotationCollector collectDefaultAnnotations(AnnotationCollector c, Annotation[] anns) { + protected final AnnotationCollector collectDefaultAnnotations(AnnotationCollector c, + Annotation[] anns) { for (int i = 0, end = anns.length; i < end; ++i) { Annotation ann = anns[i]; if (!c.isPresent(ann)) { @@ -81,7 +82,8 @@ class CollectorBase return c; } - protected final AnnotationCollector collectDefaultFromBundle(AnnotationCollector c, Annotation bundle) { + protected final AnnotationCollector collectDefaultFromBundle(AnnotationCollector c, + Annotation bundle) { Annotation[] anns = ClassUtil.findClassAnnotations(bundle.annotationType()); for (int i = 0, end = anns.length; i < end; ++i) { Annotation ann = anns[i]; diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/MemberKey.java b/src/main/java/com/fasterxml/jackson/databind/introspect/MemberKey.java index d44d885ec..58563b8cc 100644 --- a/src/main/java/com/fasterxml/jackson/databind/introspect/MemberKey.java +++ b/src/main/java/com/fasterxml/jackson/databind/introspect/MemberKey.java @@ -31,14 +31,21 @@ public final class MemberKey _argTypes = (argTypes == null) ? NO_CLASSES : argTypes; } + public String getName() { + return _name; + } + + public int argCount() { + return _argTypes.length; + } + @Override public String toString() { return _name + "(" + _argTypes.length+"-args)"; } @Override - public int hashCode() - { + public int hashCode() { return _name.hashCode() + _argTypes.length; } diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java index d36132f1f..50e1d1c60 100644 --- a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java +++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java @@ -10,12 +10,6 @@ import com.fasterxml.jackson.databind.*; public class TestMixinDeserForClass extends BaseMapTest { - /* - /********************************************************** - /* Helper bean classes - /********************************************************** - */ - static class BaseClass { /* property that is always found; but has lower priority than @@ -35,6 +29,21 @@ public class TestMixinDeserForClass @JsonAutoDetect(setterVisibility=Visibility.NONE, fieldVisibility=Visibility.NONE) interface MixIn { } + // [databind#1990] + public interface HashCodeMixIn { + @Override + @JsonProperty + public int hashCode(); + } + + public class Bean1990WithoutHashCode { + } + + public class Bean1990WithHashCode { + @Override + public int hashCode() { return 13; } + } + /* /********************************************************** /* Unit tests @@ -48,18 +57,16 @@ public class TestMixinDeserForClass LeafClass result = m.readValue("{\"a\":\"value\"}", LeafClass.class); assertEquals("XXXvalue", result.a); - /* Then with leaf-level mix-in; without (method) auto-detect, should - * use field - */ + // Then with leaf-level mix-in; without (method) auto-detect, + // should use field m = new ObjectMapper(); m.addMixIn(LeafClass.class, MixIn.class); result = m.readValue("{\"a\":\"value\"}", LeafClass.class); assertEquals("value", result.a); } - /* and then a test for mid-level mixin; should have no effect - * when deserializing leaf (but will if deserializing base class) - */ + // and then a test for mid-level mixin; should have no effect + // when deserializing leaf (but will if deserializing base class) public void testClassMixInsMidLevel() throws IOException { ObjectMapper m = new ObjectMapper(); @@ -95,4 +102,22 @@ public class TestMixinDeserForClass assertEquals("XXX", result.a); } } + + // [databind#1990]: can apply mix-in to `Object#hashCode()` to force serialization + public void testHashCodeViaObject() throws Exception + { + ObjectMapper mapper = new ObjectMapper() + .addMixIn(Object.class, HashCodeMixIn.class); + + // First, with something that overrides hashCode() + assertEquals( "{\"hashCode\":13}", + mapper.writeValueAsString(new Bean1990WithHashCode())); + + // and then special case of accessing Object#hashCode() + String prefix = "{\"hashCode\":"; + String json = mapper.writeValueAsString(new Bean1990WithoutHashCode()); + if (!json.startsWith(prefix)) { + fail("Should start with ["+prefix+"], does not: ["+json+"]"); + } + } } |