aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTatu Saloranta <tatu.saloranta@iki.fi>2018-04-19 20:39:30 -0700
committerTatu Saloranta <tatu.saloranta@iki.fi>2018-04-19 20:39:30 -0700
commit11808ccb068a75e1d2c9ae81e8c63f1fcbeb55f7 (patch)
tree607c45e9acaf7ec1c447587ecc1936d10830b661
parent69f6f52da2204839e0826a1e79ce43781bbcf0d1 (diff)
downloadjackson-databind-11808ccb068a75e1d2c9ae81e8c63f1fcbeb55f7.tar.gz
Fixed #1990
-rw-r--r--release-notes/CREDITS-2.x4
-rw-r--r--release-notes/VERSION-2.x2
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodCollector.java39
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/introspect/CollectorBase.java6
-rw-r--r--src/main/java/com/fasterxml/jackson/databind/introspect/MemberKey.java11
-rw-r--r--src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java49
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+"]");
+ }
+ }
}