| 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; |
| import com.fasterxml.jackson.databind.jsontype.TypeSerializer; |
| import com.fasterxml.jackson.databind.module.SimpleModule; |
| import com.fasterxml.jackson.databind.ser.std.StdSerializer; |
| import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; |
| |
| /** |
| * Unit tests for verifying serialization of simple basic non-structured |
| * types; primitives (and/or their wrappers), Strings. |
| */ |
| public class TestEnumSerialization |
| extends BaseMapTest |
| { |
| /* |
| /********************************************************** |
| /* Helper enums |
| /********************************************************** |
| */ |
| |
| /** |
| * Test enumeration for verifying Enum serialization functionality. |
| */ |
| protected enum TestEnum { |
| A, B, C; |
| private TestEnum() { } |
| |
| @Override public String toString() { return name().toLowerCase(); } |
| } |
| |
| /** |
| * Alternative version that forces use of "toString-serializer". |
| */ |
| @JsonSerialize(using=ToStringSerializer.class) |
| protected enum AnnotatedTestEnum { |
| A2, B2, C2; |
| private AnnotatedTestEnum() { } |
| |
| @Override public String toString() { return name().toLowerCase(); } |
| } |
| |
| protected enum EnumWithJsonValue { |
| A("foo"), B("bar"); |
| private final String name; |
| private EnumWithJsonValue(String n) { |
| name = n; |
| } |
| |
| @Override |
| public String toString() { return name; } |
| |
| @JsonValue |
| public String external() { return "value:"+name; } |
| } |
| |
| protected static interface ToStringMixin { |
| @Override |
| @JsonValue public String toString(); |
| } |
| |
| protected static enum SerializableEnum implements JsonSerializable |
| { |
| A, B, C; |
| |
| private SerializableEnum() { } |
| |
| @Override |
| public void serializeWithType(JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) |
| throws IOException, JsonProcessingException |
| { |
| serialize(jgen, provider); |
| } |
| |
| @Override |
| public void serialize(JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException |
| { |
| jgen.writeString("foo"); |
| } |
| } |
| |
| protected static enum LowerCaseEnum { |
| A, B, C; |
| private LowerCaseEnum() { } |
| @Override |
| public String toString() { return name().toLowerCase(); } |
| } |
| |
| static class MapBean { |
| public Map<TestEnum,Integer> map = new HashMap<TestEnum,Integer>(); |
| |
| public void add(TestEnum key, int value) { |
| map.put(key, Integer.valueOf(value)); |
| } |
| } |
| |
| static enum NOT_OK { |
| V1("v1"); |
| protected String key; |
| // any runtime-persistent annotation is fine |
| NOT_OK(@JsonProperty String key) { this.key = key; } |
| } |
| |
| static enum OK { |
| V1("v1"); |
| protected String key; |
| OK(String key) { this.key = key; } |
| } |
| |
| @SuppressWarnings({ "rawtypes", "serial" }) |
| static class LowerCasingEnumSerializer extends StdSerializer<Enum> |
| { |
| public LowerCasingEnumSerializer() { super(Enum.class); } |
| @Override |
| public void serialize(Enum value, JsonGenerator jgen, |
| SerializerProvider provider) throws IOException { |
| jgen.writeString(value.name().toLowerCase()); |
| } |
| } |
| |
| static enum MyEnum594 { |
| VALUE_WITH_A_REALLY_LONG_NAME_HERE("longValue"); |
| |
| private final String key; |
| private MyEnum594(String k) { key = k; } |
| |
| @JsonValue |
| public String getKey() { return key; } |
| } |
| |
| static class MyStuff594 { |
| public Map<MyEnum594,String> stuff = new EnumMap<MyEnum594,String>(MyEnum594.class); |
| |
| public MyStuff594(String value) { |
| stuff.put(MyEnum594.VALUE_WITH_A_REALLY_LONG_NAME_HERE, value); |
| } |
| } |
| |
| public class MyBean661 { |
| private Map<Foo661, String> foo = new EnumMap<Foo661, String>(Foo661.class); |
| |
| public MyBean661(String value) { |
| foo.put(Foo661.FOO, value); |
| } |
| |
| @JsonAnyGetter |
| @JsonSerialize(keyUsing = Foo661.Serializer.class) |
| public Map<Foo661, String> getFoo() { |
| return foo; |
| } |
| } |
| |
| enum Foo661 { |
| FOO; |
| public static class Serializer extends JsonSerializer<Foo661> { |
| @Override |
| public void serialize(Foo661 value, JsonGenerator jgen, SerializerProvider provider) |
| throws IOException { |
| jgen.writeFieldName("X-"+value.name()); |
| } |
| } |
| } |
| |
| protected static enum LC749Enum { |
| A, B, C; |
| private LC749Enum() { } |
| @Override |
| public String toString() { return name().toLowerCase(); } |
| } |
| |
| // for [databind#1322] |
| protected enum EnumWithJsonProperty { |
| @JsonProperty("aleph") |
| A; |
| } |
| |
| /* |
| /********************************************************** |
| /* Tests |
| /********************************************************** |
| */ |
| |
| private final ObjectMapper MAPPER = new ObjectMapper(); |
| |
| public void testSimple() throws Exception |
| { |
| assertEquals("\"B\"", MAPPER.writeValueAsString(TestEnum.B)); |
| } |
| |
| public void testEnumSet() throws Exception |
| { |
| StringWriter sw = new StringWriter(); |
| EnumSet<TestEnum> value = EnumSet.of(TestEnum.B); |
| MAPPER.writeValue(sw, value); |
| assertEquals("[\"B\"]", sw.toString()); |
| } |
| |
| /** |
| * Whereas regular Enum serializer uses enum names, some users |
| * prefer calling toString() instead. So let's verify that |
| * this can be done using annotation for enum class. |
| */ |
| public void testEnumUsingToString() throws Exception |
| { |
| StringWriter sw = new StringWriter(); |
| MAPPER.writeValue(sw, AnnotatedTestEnum.C2); |
| assertEquals("\"c2\"", sw.toString()); |
| } |
| |
| // Test [JACKSON-214] |
| public void testSubclassedEnums() throws Exception |
| { |
| assertEquals("\"B\"", MAPPER.writeValueAsString(EnumWithSubClass.B)); |
| } |
| |
| public void testEnumsWithJsonValue() throws Exception { |
| assertEquals("\"value:bar\"", MAPPER.writeValueAsString(EnumWithJsonValue.B)); |
| } |
| |
| public void testEnumsWithJsonValueUsingMixin() throws Exception |
| { |
| // can't share, as new mix-ins are added |
| ObjectMapper m = new ObjectMapper(); |
| m.addMixIn(TestEnum.class, ToStringMixin.class); |
| assertEquals("\"b\"", m.writeValueAsString(TestEnum.B)); |
| } |
| |
| // [databind#601] |
| public void testEnumsWithJsonValueInMap() throws Exception |
| { |
| EnumMap<EnumWithJsonValue,String> input = new EnumMap<EnumWithJsonValue,String>(EnumWithJsonValue.class); |
| input.put(EnumWithJsonValue.B, "x"); |
| // 24-Sep-2015, tatu: SHOULD actually use annotated method, as per: |
| assertEquals("{\"value:bar\":\"x\"}", MAPPER.writeValueAsString(input)); |
| } |
| |
| /** |
| * Test for ensuring that @JsonSerializable is used with Enum types as well |
| * as with any other types. |
| */ |
| public void testSerializableEnum() throws Exception |
| { |
| assertEquals("\"foo\"", MAPPER.writeValueAsString(SerializableEnum.A)); |
| } |
| |
| // [JACKSON-212] |
| public void testToStringEnum() throws Exception |
| { |
| ObjectMapper m = new ObjectMapper(); |
| m.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true); |
| assertEquals("\"b\"", m.writeValueAsString(LowerCaseEnum.B)); |
| |
| // [databind#749] but should also be able to dynamically disable |
| assertEquals("\"B\"", |
| m.writer().without(SerializationFeature.WRITE_ENUMS_USING_TO_STRING) |
| .writeValueAsString(LowerCaseEnum.B)); |
| } |
| |
| public void testToStringEnumWithEnumMap() throws Exception |
| { |
| ObjectMapper m = new ObjectMapper(); |
| m.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); |
| EnumMap<LowerCaseEnum,String> enums = new EnumMap<LowerCaseEnum,String>(LowerCaseEnum.class); |
| enums.put(LowerCaseEnum.C, "value"); |
| assertEquals("{\"c\":\"value\"}", m.writeValueAsString(enums)); |
| } |
| |
| public void testMapWithEnumKeys() throws Exception |
| { |
| MapBean bean = new MapBean(); |
| bean.add(TestEnum.B, 3); |
| |
| // By default Enums serialized using `name()` |
| String json = MAPPER.writeValueAsString(bean); |
| assertEquals("{\"map\":{\"B\":3}}", json); |
| |
| // but can change |
| json = MAPPER.writer() |
| .with(SerializationFeature.WRITE_ENUMS_USING_TO_STRING) |
| .writeValueAsString(bean); |
| assertEquals("{\"map\":{\"b\":3}}", json); |
| |
| // [databind#1570] |
| json = MAPPER.writer() |
| .with(SerializationFeature.WRITE_ENUMS_USING_INDEX) |
| .writeValueAsString(bean); |
| assertEquals(aposToQuotes("{'map':{'"+TestEnum.B.ordinal()+"':3}}"), json); |
| } |
| |
| public void testAsIndex() throws Exception |
| { |
| // By default, serialize using name |
| ObjectMapper m = new ObjectMapper(); |
| assertFalse(m.isEnabled(SerializationFeature.WRITE_ENUMS_USING_INDEX)); |
| assertEquals(quote("B"), m.writeValueAsString(TestEnum.B)); |
| |
| // but we can change (dynamically, too!) it to be number-based |
| m.enable(SerializationFeature.WRITE_ENUMS_USING_INDEX); |
| assertEquals("1", m.writeValueAsString(TestEnum.B)); |
| } |
| |
| // [JACKSON-757] |
| public void testAnnotationsOnEnumCtor() throws Exception |
| { |
| assertEquals(quote("V1"), MAPPER.writeValueAsString(OK.V1)); |
| assertEquals(quote("V1"), MAPPER.writeValueAsString(NOT_OK.V1)); |
| assertEquals(quote("V2"), MAPPER.writeValueAsString(NOT_OK2.V2)); |
| } |
| |
| // [Issue#227] |
| public void testGenericEnumSerializer() throws Exception |
| { |
| // By default, serialize using name |
| ObjectMapper m = new ObjectMapper(); |
| SimpleModule module = new SimpleModule("foobar"); |
| module.addSerializer(Enum.class, new LowerCasingEnumSerializer()); |
| m.registerModule(module); |
| assertEquals(quote("b"), m.writeValueAsString(TestEnum.B)); |
| } |
| |
| // [databind#594] |
| public void testJsonValueForEnumMapKey() throws Exception { |
| assertEquals(aposToQuotes("{'stuff':{'longValue':'foo'}}"), |
| MAPPER.writeValueAsString(new MyStuff594("foo"))); |
| } |
| |
| // [databind#661] |
| public void testCustomEnumMapKeySerializer() throws Exception { |
| String json = MAPPER.writeValueAsString(new MyBean661("abc")); |
| assertEquals(aposToQuotes("{'X-FOO':'abc'}"), json); |
| } |
| |
| // [databind#749] |
| |
| public void testEnumMapSerDefault() throws Exception { |
| final ObjectMapper mapper = new ObjectMapper(); |
| EnumMap<LC749Enum, String> m = new EnumMap<LC749Enum, String>(LC749Enum.class); |
| m.put(LC749Enum.A, "value"); |
| assertEquals("{\"A\":\"value\"}", mapper.writeValueAsString(m)); |
| } |
| |
| public void testEnumMapSerDisableToString() throws Exception { |
| final ObjectMapper mapper = new ObjectMapper(); |
| ObjectWriter w = mapper.writer().without(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); |
| EnumMap<LC749Enum, String> m = new EnumMap<LC749Enum, String>(LC749Enum.class); |
| m.put(LC749Enum.A, "value"); |
| assertEquals("{\"A\":\"value\"}", w.writeValueAsString(m)); |
| } |
| |
| public void testEnumMapSerEnableToString() throws Exception { |
| final ObjectMapper mapper = new ObjectMapper(); |
| ObjectWriter w = mapper.writer().with(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); |
| EnumMap<LC749Enum, String> m = new EnumMap<LC749Enum, String>(LC749Enum.class); |
| m.put(LC749Enum.A, "value"); |
| assertEquals("{\"a\":\"value\"}", w.writeValueAsString(m)); |
| } |
| |
| // [databind#1322] |
| public void testEnumsWithJsonProperty() throws Exception { |
| assertEquals(quote("aleph"), MAPPER.writeValueAsString(EnumWithJsonProperty.A)); |
| } |
| |
| // [databind#1535] |
| public void testEnumKeysWithJsonProperty() throws Exception { |
| Map<EnumWithJsonProperty,Integer> input = new HashMap<EnumWithJsonProperty,Integer>(); |
| input.put(EnumWithJsonProperty.A, 13); |
| assertEquals(aposToQuotes("{'aleph':13}"), MAPPER.writeValueAsString(input)); |
| } |
| |
| // [databind#1322] |
| public void testEnumsWithJsonPropertyInSet() throws Exception |
| { |
| assertEquals("[\"aleph\"]", |
| MAPPER.writeValueAsString(EnumSet.of(EnumWithJsonProperty.A))); |
| } |
| |
| // [databind#1322] |
| public void testEnumsWithJsonPropertyAsKey() throws Exception |
| { |
| EnumMap<EnumWithJsonProperty,String> input = new EnumMap<EnumWithJsonProperty,String>(EnumWithJsonProperty.class); |
| input.put(EnumWithJsonProperty.A, "b"); |
| assertEquals("{\"aleph\":\"b\"}", MAPPER.writeValueAsString(input)); |
| } |
| } |
| |
| // [JACKSON-757], non-inner enum |
| enum NOT_OK2 { |
| V2("v2"); |
| protected String key; |
| // any runtime-persistent annotation is fine |
| NOT_OK2(@JsonProperty String key) { this.key = key; } |
| } |