package com.fasterxml.jackson.databind.ser; import java.io.*; import java.util.*; import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.core.*; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.annotation.JsonSerialize; /** * This unit test suite tests use of Annotations for * bean serialization. */ public class TestAnnotations extends BaseMapTest { /* /********************************************************** /* Helper classes /********************************************************** */ /// Class for testing {@link JsonProperty} annotations with getters final static class SizeClassGetter { @JsonProperty public int size() { return 3; } @JsonProperty("length") public int foobar() { return -17; } // note: need not be public since there's annotation @JsonProperty protected int value() { return 0; } // dummy method; not a getter signature protected int getNotReally(int arg) { return 0; } } // And additional testing to cover [JACKSON-64] final static class SizeClassGetter2 { // Should still be considered property "x" @JsonProperty protected int getX() { return 3; } } // and some support for testing [JACKSON-120] final static class SizeClassGetter3 { // Should be considered property "y" even tho non-public @JsonSerialize protected int getY() { return 8; } } /** * Class for testing {@link JsonSerializer} annotation * for class itself. */ @JsonSerialize(using=BogusSerializer.class) final static class ClassSerializer { } /** * Class for testing an active {@link JsonSerialize#using} annotation * for a method */ final static class ClassMethodSerializer { private int _x; public ClassMethodSerializer(int x) { _x = x; } @JsonSerialize(using=StringSerializer.class) public int getX() { return _x; } } /** * Class for testing an inactive (one that will not have any effect) * {@link JsonSerialize} annotation for a method */ final static class InactiveClassMethodSerializer { private int _x; public InactiveClassMethodSerializer(int x) { _x = x; } // Basically, has no effect, hence gets serialized as number @JsonSerialize(using=JsonSerializer.None.class) public int getX() { return _x; } } /** * Class for verifying that getter information is inherited * as expected via normal class inheritance */ static class BaseBean { public int getX() { return 1; } @JsonProperty("y") private int getY() { return 2; } } static class SubClassBean extends BaseBean { public int getZ() { return 3; } } // For [JACKSON-666] ("SerializationFeature of the Beast!") @JsonPropertyOrder(alphabetic=true) static class GettersWithoutSetters { public int d = 0; @JsonCreator public GettersWithoutSetters(@JsonProperty("a") int a) { } // included, since there is a constructor property public int getA() { return 3; } // not included, as there's nothing matching public int getB() { return 4; } // include as there is setter public int getC() { return 5; } public void setC(int v) { } // and included, as there is a field public int getD() { return 6; } } // [JACKSON-806]: override 'need-setter' with explicit annotation static class GettersWithoutSetters2 { @JsonProperty public int getA() { return 123; } } /* /********************************************************** /* Other helper classes /********************************************************** */ public final static class BogusSerializer extends JsonSerializer<Object> { @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException { jgen.writeBoolean(true); } } private final static class StringSerializer extends JsonSerializer<Object> { @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException { jgen.writeString("X"+value+"X"); } } /* /********************************************************** /* Main tests /********************************************************** */ private final ObjectMapper MAPPER = new ObjectMapper(); public void testSimpleGetter() throws Exception { Map<String,Object> result = writeAndMap(MAPPER, new SizeClassGetter()); assertEquals(3, result.size()); assertEquals(Integer.valueOf(3), result.get("size")); assertEquals(Integer.valueOf(-17), result.get("length")); assertEquals(Integer.valueOf(0), result.get("value")); } public void testSimpleGetter2() throws Exception { Map<String,Object> result = writeAndMap(MAPPER, new SizeClassGetter2()); assertEquals(1, result.size()); assertEquals(Integer.valueOf(3), result.get("x")); } // testing [JACKSON-120], implied getter public void testSimpleGetter3() throws Exception { Map<String,Object> result = writeAndMap(MAPPER, new SizeClassGetter3()); assertEquals(1, result.size()); assertEquals(Integer.valueOf(8), result.get("y")); } /** * Let's also verify that inherited super-class getters are used * as expected */ public void testGetterInheritance() throws Exception { Map<String,Object> result = writeAndMap(MAPPER, new SubClassBean()); assertEquals(3, result.size()); assertEquals(Integer.valueOf(1), result.get("x")); assertEquals(Integer.valueOf(2), result.get("y")); assertEquals(Integer.valueOf(3), result.get("z")); } /** * Unit test to verify that {@link JsonSerialize#using} annotation works * when applied to a class */ public void testClassSerializer() throws Exception { StringWriter sw = new StringWriter(); MAPPER.writeValue(sw, new ClassSerializer()); assertEquals("true", sw.toString()); } /** * Unit test to verify that @JsonSerializer annotation works * when applied to a Method */ public void testActiveMethodSerializer() throws Exception { StringWriter sw = new StringWriter(); MAPPER.writeValue(sw, new ClassMethodSerializer(13)); /* Here we will get wrapped as an object, since we have * full object, just override a single property */ assertEquals("{\"x\":\"X13X\"}", sw.toString()); } public void testInactiveMethodSerializer() throws Exception { String json = MAPPER.writeValueAsString(new InactiveClassMethodSerializer(8)); /* Here we will get wrapped as an object, since we have * full object, just override a single property */ assertEquals("{\"x\":8}", json); } public void testGettersWithoutSetters() throws Exception { ObjectMapper m = new ObjectMapper(); GettersWithoutSetters bean = new GettersWithoutSetters(123); assertFalse(m.isEnabled(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS)); // by default, all 4 found: assertEquals("{\"a\":3,\"b\":4,\"c\":5,\"d\":6}", m.writeValueAsString(bean)); // but 3 if we require mutator: m = new ObjectMapper(); m.enable(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS); assertEquals("{\"a\":3,\"c\":5,\"d\":6}", m.writeValueAsString(bean)); } public void testGettersWithoutSettersOverride() throws Exception { GettersWithoutSetters2 bean = new GettersWithoutSetters2(); ObjectMapper m = new ObjectMapper(); m.enable(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS); assertEquals("{\"a\":123}", m.writeValueAsString(bean)); } }