package com.fasterxml.jackson.databind.creators; import java.math.BigDecimal; import java.util.*; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.deser.Deserializers; import com.fasterxml.jackson.databind.deser.std.EnumDeserializer; import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; import com.fasterxml.jackson.databind.module.SimpleModule; public class EnumCreatorTest extends BaseMapTest { 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 EnumWithBDCreator { E5, E8; @JsonCreator public static EnumWithBDCreator create(BigDecimal bd) { if (bd.longValue() == 5L) return E5; if (bd.longValue() == 8L) return E8; return null; } } protected enum TestEnumFromInt { ENUM_A(1), ENUM_B(2), ENUM_C(3); private final int id; private TestEnumFromInt(int id) { this.id = id; } @JsonCreator public static TestEnumFromInt fromId(int id) { for (TestEnumFromInt e: values()) { if (e.id == id) return e; } return null; } } static enum EnumWithPropertiesModeJsonCreator { TEST1, TEST2, TEST3; @JsonGetter("name") public String getName() { return name(); } @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) public static EnumWithPropertiesModeJsonCreator create(@JsonProperty("name") String name) { return EnumWithPropertiesModeJsonCreator.valueOf(name); } } static enum EnumWithDelegateModeJsonCreator { TEST1, TEST2, TEST3; @JsonGetter("name") public String getName() { return name(); } @JsonCreator(mode = JsonCreator.Mode.DELEGATING) public static EnumWithDelegateModeJsonCreator create(JsonNode json) { return EnumWithDelegateModeJsonCreator.valueOf(json.get("name").asText()); } } // [databind#324]: exception from creator method protected enum TestEnum324 { A, B; @JsonCreator public static TestEnum324 creator(String arg) { throw new RuntimeException("Foobar!"); } } // [databind#745] static class DelegatingDeserializers extends Deserializers.Base { @Override public JsonDeserializer<?> findEnumDeserializer(final Class<?> type, final DeserializationConfig config, final BeanDescription beanDesc) throws JsonMappingException { final Collection<AnnotatedMethod> factoryMethods = beanDesc.getFactoryMethods(); if (factoryMethods != null) { for (AnnotatedMethod am : factoryMethods) { final JsonCreator creator = am.getAnnotation(JsonCreator.class); if (creator != null) { return EnumDeserializer.deserializerForCreator(config, type, am, null, null); } } } return null; } } // [databind#745] static class DelegatingDeserializersModule extends SimpleModule { private static final long serialVersionUID = 1L; @Override public void setupModule(final SetupContext context) { context.addDeserializers(new DelegatingDeserializers()); } } // [databind#929]: support multi-arg enum creator static enum Enum929 { A, B, C; @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) static Enum929 forValues(@JsonProperty("id") int intProp, @JsonProperty("name") String name) { return Enum929.valueOf(name); } } static enum MyEnum960 { VALUE, BOGUS; @JsonCreator public static MyEnum960 getInstance() { return VALUE; } } static class MyEnum960Wrapper { public MyEnum960 value; } static enum Enum1291 { V1("val1"), V2("val2"), V3("val3"), V4("val4"), V5("val5"), V6("val6"); private final String name; Enum1291(String name) { this.name = name; } public static Enum1291 fromString(String name) { for (Enum1291 type : Enum1291.values()) { if (type.name.equals(name)) { return type; } } return Enum1291.valueOf(name.toUpperCase()); } @Override public String toString() { return name; } } /* /********************************************************** /* Test methods /********************************************************** */ protected final ObjectMapper MAPPER = new ObjectMapper(); public void testCreatorEnums() throws Exception { EnumWithCreator value = MAPPER.readValue("\"enumA\"", EnumWithCreator.class); assertEquals(EnumWithCreator.A, value); } public void testCreatorEnumsFromBigDecimal() throws Exception { EnumWithBDCreator value = MAPPER.readValue("\"8.0\"", EnumWithBDCreator.class); assertEquals(EnumWithBDCreator.E8, value); } 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 { 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)); } public void testJsonCreatorPropertiesWithEnum() throws Exception { EnumWithPropertiesModeJsonCreator type1 = MAPPER.readValue("{\"name\":\"TEST1\", \"description\":\"TEST\"}", EnumWithPropertiesModeJsonCreator.class); assertSame(EnumWithPropertiesModeJsonCreator.TEST1, type1); EnumWithPropertiesModeJsonCreator type2 = MAPPER.readValue("{\"name\":\"TEST3\", \"description\":\"TEST\"}", EnumWithPropertiesModeJsonCreator.class); assertSame(EnumWithPropertiesModeJsonCreator.TEST3, type2); } public void testJsonCreatorDelagateWithEnum() throws Exception { final ObjectMapper mapper = new ObjectMapper(); EnumWithDelegateModeJsonCreator type1 = mapper.readValue("{\"name\":\"TEST1\", \"description\":\"TEST\"}", EnumWithDelegateModeJsonCreator.class); assertSame(EnumWithDelegateModeJsonCreator.TEST1, type1); EnumWithDelegateModeJsonCreator type2 = mapper.readValue("{\"name\":\"TEST3\", \"description\":\"TEST\"}", EnumWithDelegateModeJsonCreator.class); assertSame(EnumWithDelegateModeJsonCreator.TEST3, type2); } public void testEnumsFromInts() throws Exception { Object ob = MAPPER.readValue("1 ", TestEnumFromInt.class); assertEquals(TestEnumFromInt.class, ob.getClass()); assertSame(TestEnumFromInt.ENUM_A, ob); } // [databind#324] public void testExceptionFromCreator() throws Exception { try { /*TestEnum324 e =*/ MAPPER.readValue(quote("xyz"), TestEnum324.class); fail("Should throw exception"); } catch (JsonMappingException e) { verifyException(e, "foobar"); } } // [databind#745] public void testDeserializerForCreatorWithEnumMaps() throws Exception { final ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new DelegatingDeserializersModule()); EnumMap<EnumWithCreator,String> value = mapper.readValue("{\"enumA\":\"value\"}", new TypeReference<EnumMap<EnumWithCreator,String>>() {}); assertEquals("value", value.get(EnumWithCreator.A)); } // for [databind#929] public void testMultiArgEnumCreator() throws Exception { Enum929 v = MAPPER.readValue("{\"id\":3,\"name\":\"B\"}", Enum929.class); assertEquals(Enum929.B, v); } // for [databind#960] public void testNoArgEnumCreator() throws Exception { MyEnum960 v = MAPPER.readValue("{\"value\":\"bogus\"}", MyEnum960.class); assertEquals(MyEnum960.VALUE, v); } // for [databind#1291] public void testEnumCreators1291() throws Exception { ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(Enum1291.V2); Enum1291 result = mapper.readValue(json, Enum1291.class); assertSame(Enum1291.V2, result); } // for [databind#1389] public void testMultiArgEnumInCollections() throws Exception { EnumSet<Enum929> valueEnumSet = MAPPER.readValue("[{\"id\":3,\"name\":\"B\"}, {\"id\":3,\"name\":\"A\"}]", new TypeReference<EnumSet<Enum929>>() {}); assertEquals(2, valueEnumSet.size()); assertTrue(valueEnumSet.contains(Enum929.A)); assertTrue(valueEnumSet.contains(Enum929.B)); List<Enum929> valueList = MAPPER.readValue("[{\"id\":3,\"name\":\"B\"}, {\"id\":3,\"name\":\"A\"}, {\"id\":3,\"name\":\"B\"}]", new TypeReference<List<Enum929>>() {}); assertEquals(3, valueList.size()); assertEquals(Enum929.B, valueList.get(2)); } }