package com.fasterxml.jackson.databind.mixins; import java.io.*; import java.util.*; import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.introspect.ClassIntrospector; import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver; public class TestMixinSerForMethods extends BaseMapTest { /* /********************************************************** /* Helper bean classes /********************************************************** */ // base class: just one visible property ('b') @SuppressWarnings("unused") static class BaseClass { private String a; private String b; protected BaseClass() { } public BaseClass(String a, String b) { this.a = a; this.b = b; } @JsonProperty("b") public String takeB() { return b; } } /* extends, just for fun; and to show possible benefit of being * able to declare that a method is overridden (compile-time check * that our intended mix-in override will match a method) */ abstract static class MixIn extends BaseClass { // let's make 'a' visible @JsonProperty String a; @Override @JsonProperty("b2") public abstract String takeB(); // also: just for fun; add a "red herring"... unmatched method @JsonProperty abstract String getFoobar(); } static class LeafClass extends BaseClass { public LeafClass(String a, String b) { super(a, b); } @Override @JsonIgnore public String takeB() { return null; } } static class EmptyBean { } static class SimpleBean extends EmptyBean { int x() { return 42; } } /** * This mix-in is to be attached to EmptyBean, but really modify * methods that its subclass, SimpleBean, has. */ abstract class MixInForSimple { // This should apply to sub-class @JsonProperty("x") abstract int x(); // and this matches nothing, should be ignored @JsonProperty("notreally") public int xxx() { return 3; } // nor this public abstract int getIt(); } /* /********************************************************** /* Unit tests /********************************************************** */ /** * Unit test for verifying that leaf-level mix-ins work ok; * that is, any annotations added properly override all annotations * that masked methods (fields etc) have. */ public void testLeafMixin() throws IOException { ObjectMapper mapper = new ObjectMapper(); Map<String,Object> result; BaseClass bean = new BaseClass("a1", "b2"); // first: with no mix-ins: result = writeAndMap(mapper, bean); assertEquals(1, result.size()); assertEquals("b2", result.get("b")); // then with leaf-level mix-in mapper = new ObjectMapper(); mapper.addMixIn(BaseClass.class, MixIn.class); result = writeAndMap(mapper, bean); assertEquals(2, result.size()); assertEquals("b2", result.get("b2")); assertEquals("a1", result.get("a")); } /** * Unit test for verifying that having a mix-in "between" classes * (overriding annotations of a base class, but being overridden * further by a sub-class) works as expected */ public void testIntermediateMixin() throws IOException { ObjectMapper mapper = new ObjectMapper(); Map<String,Object> result; LeafClass bean = new LeafClass("XXX", "b2"); mapper.addMixIn(BaseClass.class, MixIn.class); result = writeAndMap(mapper, bean); assertEquals(1, result.size()); assertEquals("XXX", result.get("a")); } /** * Another intermediate mix-in, to verify that annotations * properly "trickle up" */ public void testIntermediateMixin2() throws IOException { ObjectMapper mapper = new ObjectMapper(); mapper.addMixIn(EmptyBean.class, MixInForSimple.class); Map<String,Object> result = writeAndMap(mapper, new SimpleBean()); assertEquals(1, result.size()); assertEquals(Integer.valueOf(42), result.get("x")); } // [databind#688] public void testCustomResolver() throws IOException { ObjectMapper mapper = new ObjectMapper(); mapper.setMixInResolver(new ClassIntrospector.MixInResolver() { @Override public Class<?> findMixInClassFor(Class<?> target) { if (target == EmptyBean.class) { return MixInForSimple.class; } return null; } @Override public MixInResolver copy() { return this; } }); Map<String,Object> result = writeAndMap(mapper, new SimpleBean()); assertEquals(1, result.size()); assertEquals(Integer.valueOf(42), result.get("x")); } }