blob: 2580ae5ea3cbc28c6c0f295656047aa001c9ad5e [file] [log] [blame]
package com.fasterxml.jackson.databind.deser;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.concurrent.TimeUnit;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
public class TestEnumDeserialization
extends BaseMapTest
{
/*
/**********************************************************
/* Helper classes, enums
/**********************************************************
*/
enum TestEnum { JACKSON, RULES, OK; }
/**
* Alternative version that annotates which deserializer to use
*/
@JsonDeserialize(using=DummySerializer.class)
enum AnnotatedTestEnum {
JACKSON, RULES, OK;
}
public static class DummySerializer extends StdDeserializer<Object>
{
public DummySerializer() { super(Object.class); }
@Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt)
{
return AnnotatedTestEnum.OK;
}
}
protected enum EnumWithCreator {
A, B;
@JsonCreator
public static EnumWithCreator fromEnum(String str) {
if ("enumA".equals(str)) return A;
if ("enumB".equals(str)) return B;
return null;
}
}
protected enum LowerCaseEnum {
A, B, C;
private LowerCaseEnum() { }
@Override
public String toString() { return name().toLowerCase(); }
}
// for [JACKSON-749]
protected enum EnumWithJsonValue {
A("foo"), B("bar");
private final String name;
private EnumWithJsonValue(String n) {
name = n;
}
@JsonValue
@Override
public String toString() { return name; }
}
/*
/**********************************************************
/* Tests
/**********************************************************
*/
protected final ObjectMapper mapper = new ObjectMapper();
public void testSimple() throws Exception
{
// First "good" case with Strings
String JSON = "\"OK\" \"RULES\" null";
// multiple main-level mappings, need explicit parser:
JsonParser jp = mapper.getJsonFactory().createJsonParser(JSON);
assertEquals(TestEnum.OK, mapper.readValue(jp, TestEnum.class));
assertEquals(TestEnum.RULES, mapper.readValue(jp, TestEnum.class));
/* should be ok; nulls are typeless; handled by mapper, not by
* deserializer
*/
assertNull(mapper.readValue(jp, TestEnum.class));
// and no more content beyond that...
assertFalse(jp.hasCurrentToken());
/* Then alternative with index (0 means first entry)
*/
assertEquals(TestEnum.JACKSON, mapper.readValue(" 0 ", TestEnum.class));
/* Then error case: unrecognized value
*/
try {
/*Object result =*/ mapper.readValue("\"NO-SUCH-VALUE\"", TestEnum.class);
fail("Expected an exception for bogus enum value...");
} catch (JsonMappingException jex) {
verifyException(jex, "value not one of declared");
}
}
/**
* Enums are considered complex if they have code (and hence sub-classes)... an
* example is TimeUnit
*/
public void testComplexEnum() throws Exception
{
String json = mapper.writeValueAsString(TimeUnit.HOURS);
assertEquals(quote("HOURS"), json);
TimeUnit result = mapper.readValue(json, TimeUnit.class);
assertSame(TimeUnit.HOURS, result);
}
/**
* Testing to see that annotation override works
*/
public void testAnnotated() throws Exception
{
AnnotatedTestEnum e = mapper.readValue("\"JACKSON\"", AnnotatedTestEnum.class);
/* dummy deser always returns value OK, independent of input;
* only works if annotation is used
*/
assertEquals(AnnotatedTestEnum.OK, e);
}
public void testEnumMaps() throws Exception
{
EnumMap<TestEnum,String> value = mapper.readValue("{\"OK\":\"value\"}",
new TypeReference<EnumMap<TestEnum,String>>() { });
assertEquals("value", value.get(TestEnum.OK));
}
// Test [JACKSON-214]
public void testSubclassedEnums() throws Exception
{
EnumWithSubClass value = mapper.readValue("\"A\"", EnumWithSubClass.class);
assertEquals(EnumWithSubClass.A, value);
}
// [JACKSON-193]
public void testCreatorEnums() throws Exception
{
EnumWithCreator value = mapper.readValue("\"enumA\"", EnumWithCreator.class);
assertEquals(EnumWithCreator.A, value);
}
// [JACKSON-212]
public void testToStringEnums() throws Exception
{
// can't reuse global one due to reconfig
ObjectMapper m = new ObjectMapper();
m.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
LowerCaseEnum value = m.readValue("\"c\"", LowerCaseEnum.class);
assertEquals(LowerCaseEnum.C, value);
}
// [JACKSON-212]
public void testToStringEnumMaps() throws Exception
{
// can't reuse global one due to reconfig
ObjectMapper m = new ObjectMapper();
m.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
EnumMap<LowerCaseEnum,String> value = m.readValue("{\"a\":\"value\"}",
new TypeReference<EnumMap<LowerCaseEnum,String>>() { });
assertEquals("value", value.get(LowerCaseEnum.A));
}
// [JACKSON-412], disallow use of numbers
public void testNumbersToEnums() throws Exception
{
// by default numbers are fine:
assertFalse(mapper.getDeserializationConfig().isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS));
TestEnum value = mapper.readValue("1", TestEnum.class);
assertSame(TestEnum.RULES, value);
// but can also be changed to errors:
ObjectMapper m = new ObjectMapper();
m.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, true);
try {
value = m.readValue("1", TestEnum.class);
fail("Expected an error");
} catch (JsonMappingException e) {
verifyException(e, "Not allowed to deserialize Enum value out of JSON number");
}
}
// [JACKSON-684], enums using index
public void testEnumsWithIndex() throws Exception
{
ObjectMapper m = new ObjectMapper();
m.enable(SerializationFeature.WRITE_ENUMS_USING_INDEX);
String json = m.writeValueAsString(TestEnum.RULES);
assertEquals(String.valueOf(TestEnum.RULES.ordinal()), json);
TestEnum result = m.readValue(json, TestEnum.class);
assertSame(TestEnum.RULES, result);
}
// [JACKSON-749]: @JsonValue should be considered as well
public void testEnumsWithJsonValue() throws Exception
{
// first, enum as is
EnumWithJsonValue e = mapper.readValue(quote("foo"), EnumWithJsonValue.class);
assertSame(EnumWithJsonValue.A, e);
e = mapper.readValue(quote("bar"), EnumWithJsonValue.class);
assertSame(EnumWithJsonValue.B, e);
// then in EnumSet
EnumSet<EnumWithJsonValue> set = mapper.readValue("[\"bar\"]",
new TypeReference<EnumSet<EnumWithJsonValue>>() { });
assertNotNull(set);
assertEquals(1, set.size());
assertTrue(set.contains(EnumWithJsonValue.B));
assertFalse(set.contains(EnumWithJsonValue.A));
// and finally EnumMap
EnumMap<EnumWithJsonValue,Integer> map = mapper.readValue("{\"foo\":13}",
new TypeReference<EnumMap<EnumWithJsonValue, Integer>>() { });
assertNotNull(map);
assertEquals(1, map.size());
assertEquals(Integer.valueOf(13), map.get(EnumWithJsonValue.A));
}
// [JACKSON-756], next three tests
public void testEnumWithCreatorEnumMaps() throws Exception {
EnumMap<EnumWithCreator,String> value = mapper.readValue("{\"enumA\":\"value\"}",
new TypeReference<EnumMap<EnumWithCreator,String>>() {});
assertEquals("value", value.get(EnumWithCreator.A));
}
public void testEnumWithCreatorMaps() throws Exception {
java.util.HashMap<EnumWithCreator,String> value = mapper.readValue("{\"enumA\":\"value\"}",
new TypeReference<java.util.HashMap<EnumWithCreator,String>>() {});
assertEquals("value", value.get(EnumWithCreator.A));
}
public void testEnumWithCreatorEnumSets() throws Exception {
EnumSet<EnumWithCreator> value = mapper.readValue("[\"enumA\"]",
new TypeReference<EnumSet<EnumWithCreator>>() {});
assertTrue(value.contains(EnumWithCreator.A));
}
}