• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind.ser;
2 
3 import java.io.*;
4 import java.util.*;
5 
6 import com.fasterxml.jackson.annotation.*;
7 
8 import com.fasterxml.jackson.core.*;
9 import com.fasterxml.jackson.databind.*;
10 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
11 import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
12 import com.fasterxml.jackson.databind.module.SimpleModule;
13 import com.fasterxml.jackson.databind.ser.std.StdSerializer;
14 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
15 
16 /**
17  * Unit tests for verifying serialization of simple basic non-structured
18  * types; primitives (and/or their wrappers), Strings.
19  */
20 public class TestEnumSerialization
21     extends BaseMapTest
22 {
23     /**
24      * Test enumeration for verifying Enum serialization functionality.
25      */
26     protected enum TestEnum {
27         A, B, C;
TestEnum()28         private TestEnum() { }
29 
toString()30         @Override public String toString() { return name().toLowerCase(); }
31     }
32 
33     /**
34      * Alternative version that forces use of "toString-serializer".
35      */
36     @JsonSerialize(using=ToStringSerializer.class)
37     protected enum AnnotatedTestEnum {
38         A2, B2, C2;
AnnotatedTestEnum()39         private AnnotatedTestEnum() { }
40 
toString()41         @Override public String toString() { return name().toLowerCase(); }
42     }
43 
44     protected enum EnumWithJsonValue {
45         A("foo"), B("bar");
46         private final String name;
EnumWithJsonValue(String n)47         private EnumWithJsonValue(String n) {
48             name = n;
49         }
50 
51         @Override
toString()52         public String toString() { return name; }
53 
54         @JsonValue
external()55         public String external() { return "value:"+name; }
56     }
57 
58     protected static interface ToStringMixin {
59         @Override
toString()60         @JsonValue public String toString();
61     }
62 
63     protected static enum SerializableEnum implements JsonSerializable
64     {
65         A, B, C;
66 
SerializableEnum()67         private SerializableEnum() { }
68 
69         @Override
serializeWithType(JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer)70         public void serializeWithType(JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer)
71                 throws IOException, JsonProcessingException
72         {
73             serialize(jgen, provider);
74         }
75 
76         @Override
serialize(JsonGenerator jgen, SerializerProvider provider)77         public void serialize(JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException
78         {
79             jgen.writeString("foo");
80         }
81     }
82 
83     protected static enum LowerCaseEnum {
84         A, B, C;
LowerCaseEnum()85         private LowerCaseEnum() { }
86         @Override
toString()87         public String toString() { return name().toLowerCase(); }
88     }
89 
90     static class MapBean {
91         public Map<TestEnum,Integer> map = new HashMap<TestEnum,Integer>();
92 
add(TestEnum key, int value)93         public void add(TestEnum key, int value) {
94             map.put(key, Integer.valueOf(value));
95         }
96     }
97 
98     static enum NOT_OK {
99         V1("v1");
100         protected String key;
101         // any runtime-persistent annotation is fine
NOT_OK(@sonProperty String key)102         NOT_OK(@JsonProperty String key) { this.key = key; }
103     }
104 
105     static enum OK {
106         V1("v1");
107         protected String key;
OK(String key)108         OK(String key) { this.key = key; }
109     }
110 
111     @SuppressWarnings({ "rawtypes", "serial" })
112     static class LowerCasingEnumSerializer extends StdSerializer<Enum>
113     {
LowerCasingEnumSerializer()114         public LowerCasingEnumSerializer() { super(Enum.class); }
115         @Override
serialize(Enum value, JsonGenerator jgen, SerializerProvider provider)116         public void serialize(Enum value, JsonGenerator jgen,
117                 SerializerProvider provider) throws IOException {
118             jgen.writeString(value.name().toLowerCase());
119         }
120     }
121 
122     protected static enum LC749Enum {
123         A, B, C;
LC749Enum()124         private LC749Enum() { }
125         @Override
toString()126         public String toString() { return name().toLowerCase(); }
127     }
128 
129     // for [databind#1322]
130     protected enum EnumWithJsonProperty {
131         @JsonProperty("aleph")
132         A;
133     }
134 
135     /*
136     /**********************************************************************
137     /* Test methods
138     /**********************************************************************
139      */
140 
141     private final ObjectMapper MAPPER = newJsonMapper();
142 
testSimple()143     public void testSimple() throws Exception
144     {
145         assertEquals("\"B\"", MAPPER.writeValueAsString(TestEnum.B));
146     }
147 
testEnumSet()148     public void testEnumSet() throws Exception
149     {
150         final EnumSet<TestEnum> value = EnumSet.of(TestEnum.B);
151         assertEquals("[\"B\"]", MAPPER.writeValueAsString(value));
152     }
153 
154     /**
155      * Whereas regular Enum serializer uses enum names, some users
156      * prefer calling toString() instead. So let's verify that
157      * this can be done using annotation for enum class.
158      */
testEnumUsingToString()159     public void testEnumUsingToString() throws Exception
160     {
161         assertEquals("\"c2\"", MAPPER.writeValueAsString(AnnotatedTestEnum.C2));
162     }
163 
testSubclassedEnums()164     public void testSubclassedEnums() throws Exception
165     {
166         assertEquals("\"B\"", MAPPER.writeValueAsString(EnumWithSubClass.B));
167     }
168 
testEnumsWithJsonValue()169     public void testEnumsWithJsonValue() throws Exception {
170         assertEquals("\"value:bar\"", MAPPER.writeValueAsString(EnumWithJsonValue.B));
171     }
172 
testEnumsWithJsonValueUsingMixin()173     public void testEnumsWithJsonValueUsingMixin() throws Exception
174     {
175         // can't share, as new mix-ins are added
176         ObjectMapper m = new ObjectMapper();
177         m.addMixIn(TestEnum.class, ToStringMixin.class);
178         assertEquals("\"b\"", m.writeValueAsString(TestEnum.B));
179     }
180 
181     // [databind#601]
testEnumsWithJsonValueInMap()182     public void testEnumsWithJsonValueInMap() throws Exception
183     {
184         EnumMap<EnumWithJsonValue,String> input = new EnumMap<EnumWithJsonValue,String>(EnumWithJsonValue.class);
185         input.put(EnumWithJsonValue.B, "x");
186         // 24-Sep-2015, tatu: SHOULD actually use annotated method, as per:
187         assertEquals("{\"value:bar\":\"x\"}", MAPPER.writeValueAsString(input));
188     }
189 
190     /**
191      * Test for ensuring that @JsonSerializable is used with Enum types as well
192      * as with any other types.
193      */
testSerializableEnum()194     public void testSerializableEnum() throws Exception
195     {
196         assertEquals("\"foo\"", MAPPER.writeValueAsString(SerializableEnum.A));
197     }
198 
testToStringEnum()199     public void testToStringEnum() throws Exception
200     {
201         ObjectMapper m = new ObjectMapper();
202         m.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
203         assertEquals("\"b\"", m.writeValueAsString(LowerCaseEnum.B));
204 
205         // [databind#749] but should also be able to dynamically disable
206         assertEquals("\"B\"",
207                 m.writer().without(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)
208                     .writeValueAsString(LowerCaseEnum.B));
209     }
210 
testToStringEnumWithEnumMap()211     public void testToStringEnumWithEnumMap() throws Exception
212     {
213         ObjectMapper m = new ObjectMapper();
214         m.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
215         EnumMap<LowerCaseEnum,String> enums = new EnumMap<LowerCaseEnum,String>(LowerCaseEnum.class);
216         enums.put(LowerCaseEnum.C, "value");
217         assertEquals("{\"c\":\"value\"}", m.writeValueAsString(enums));
218     }
219 
testAsIndex()220     public void testAsIndex() throws Exception
221     {
222         // By default, serialize using name
223         ObjectMapper m = new ObjectMapper();
224         assertFalse(m.isEnabled(SerializationFeature.WRITE_ENUMS_USING_INDEX));
225         assertEquals(quote("B"), m.writeValueAsString(TestEnum.B));
226 
227         // but we can change (dynamically, too!) it to be number-based
228         m.enable(SerializationFeature.WRITE_ENUMS_USING_INDEX);
229         assertEquals("1", m.writeValueAsString(TestEnum.B));
230     }
231 
testAnnotationsOnEnumCtor()232     public void testAnnotationsOnEnumCtor() throws Exception
233     {
234         assertEquals(quote("V1"), MAPPER.writeValueAsString(OK.V1));
235         assertEquals(quote("V1"), MAPPER.writeValueAsString(NOT_OK.V1));
236         assertEquals(quote("V2"), MAPPER.writeValueAsString(NOT_OK2.V2));
237     }
238 
239     // [databind#227]
testGenericEnumSerializer()240     public void testGenericEnumSerializer() throws Exception
241     {
242         // By default, serialize using name
243         ObjectMapper m = new ObjectMapper();
244         SimpleModule module = new SimpleModule("foobar");
245         module.addSerializer(Enum.class, new LowerCasingEnumSerializer());
246         m.registerModule(module);
247         assertEquals(quote("b"), m.writeValueAsString(TestEnum.B));
248     }
249 
250     // [databind#749]
251 
testEnumMapSerDefault()252     public void testEnumMapSerDefault() throws Exception {
253         final ObjectMapper mapper = newJsonMapper();
254         EnumMap<LC749Enum, String> m = new EnumMap<LC749Enum, String>(LC749Enum.class);
255         m.put(LC749Enum.A, "value");
256         assertEquals("{\"A\":\"value\"}", mapper.writeValueAsString(m));
257     }
258 
testEnumMapSerDisableToString()259     public void testEnumMapSerDisableToString() throws Exception {
260         final ObjectMapper mapper = new ObjectMapper();
261         ObjectWriter w = mapper.writer().without(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
262         EnumMap<LC749Enum, String> m = new EnumMap<LC749Enum, String>(LC749Enum.class);
263         m.put(LC749Enum.A, "value");
264         assertEquals("{\"A\":\"value\"}", w.writeValueAsString(m));
265     }
266 
testEnumMapSerEnableToString()267     public void testEnumMapSerEnableToString() throws Exception {
268         final ObjectMapper mapper = new ObjectMapper();
269         ObjectWriter w = mapper.writer().with(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
270         EnumMap<LC749Enum, String> m = new EnumMap<LC749Enum, String>(LC749Enum.class);
271         m.put(LC749Enum.A, "value");
272         assertEquals("{\"a\":\"value\"}", w.writeValueAsString(m));
273     }
274 
275     // [databind#1322]
testEnumsWithJsonProperty()276     public void testEnumsWithJsonProperty() throws Exception {
277         assertEquals(quote("aleph"), MAPPER.writeValueAsString(EnumWithJsonProperty.A));
278     }
279 
280     // [databind#1535]
testEnumKeysWithJsonProperty()281     public void testEnumKeysWithJsonProperty() throws Exception {
282         Map<EnumWithJsonProperty,Integer> input = new HashMap<EnumWithJsonProperty,Integer>();
283         input.put(EnumWithJsonProperty.A, 13);
284         assertEquals(aposToQuotes("{'aleph':13}"), MAPPER.writeValueAsString(input));
285     }
286 
287     // [databind#1322]
testEnumsWithJsonPropertyInSet()288     public void testEnumsWithJsonPropertyInSet() throws Exception
289     {
290         assertEquals("[\"aleph\"]",
291                 MAPPER.writeValueAsString(EnumSet.of(EnumWithJsonProperty.A)));
292     }
293 
294     // [databind#1322]
testEnumsWithJsonPropertyAsKey()295     public void testEnumsWithJsonPropertyAsKey() throws Exception
296     {
297         EnumMap<EnumWithJsonProperty,String> input = new EnumMap<EnumWithJsonProperty,String>(EnumWithJsonProperty.class);
298         input.put(EnumWithJsonProperty.A, "b");
299         assertEquals("{\"aleph\":\"b\"}", MAPPER.writeValueAsString(input));
300     }
301 }
302 
303 // [JACKSON-757], non-inner enum
304 enum NOT_OK2 {
305     V2("v2");
306     protected String key;
307     // any runtime-persistent annotation is fine
NOT_OK2(@sonProperty String key)308     NOT_OK2(@JsonProperty String key) { this.key = key; }
309 }
310