1 package com.fasterxml.jackson.databind.jsontype; 2 3 import java.io.IOException; 4 import java.util.LinkedHashMap; 5 import java.util.Map; 6 7 import com.fasterxml.jackson.annotation.*; 8 import com.fasterxml.jackson.core.JsonGenerator; 9 import com.fasterxml.jackson.databind.*; 10 import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 11 import com.fasterxml.jackson.databind.annotation.JsonSerialize; 12 import com.fasterxml.jackson.databind.testutil.NoCheckSubTypeValidator; 13 14 public class TypeRefinementForMapTest extends BaseMapTest 15 { 16 interface HasUniqueId<K> { getId()17 K getId(); 18 } 19 20 static class Item implements HasUniqueId<String> 21 { 22 public String id; 23 public String property; 24 25 @Override getId()26 public String getId() { return id; } 27 } 28 29 static class Data 30 { 31 public String id; 32 33 @JsonDeserialize(as = MyHashMap.class) 34 public Map<String, Item> items; 35 36 // Would work with straight arguments: 37 // public MyHashMap<String, Item> items; 38 } 39 40 @SuppressWarnings("serial") 41 static class MyHashMap<K, V extends HasUniqueId<K>> 42 extends LinkedHashMap<K, V> 43 { 44 @JsonCreator(mode=JsonCreator.Mode.DELEGATING) MyHashMap(V[] values)45 public MyHashMap(V[] values) { 46 for (int i = 0; i < values.length; i++) { 47 V v = values[i]; 48 put(v.getId(), v); 49 } 50 } 51 } 52 53 // for [databind#1384] 54 @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) 55 public static final class TestClass { 56 @JsonProperty("mapProperty") 57 @JsonSerialize(keyUsing = CompoundKeySerializer.class) 58 @JsonDeserialize(keyUsing = CompoundKeyDeserializer.class) 59 private final Map<CompoundKey, String> mapProperty; 60 61 @JsonCreator TestClass(@sonProperty"mapProperty") Map<CompoundKey, String> mapProperty)62 private TestClass(@JsonProperty("mapProperty") Map<CompoundKey, String> mapProperty) { 63 this.mapProperty = mapProperty; 64 } 65 } 66 67 static final class CompoundKey { 68 private String part0; 69 private String part1; 70 CompoundKey(String part0, String part1)71 public CompoundKey(String part0, String part1) { 72 this.part0 = part0; 73 this.part1 = part1; 74 } 75 getPart0()76 public String getPart0() { return part0; } getPart1()77 public String getPart1() { return part1; } 78 } 79 80 static class CompoundKeyDeserializer extends KeyDeserializer { 81 @Override deserializeKey(String s, DeserializationContext deserializationContext)82 public Object deserializeKey(String s, DeserializationContext deserializationContext) { 83 String[] parts = s.split("\\|"); 84 return new CompoundKey(parts[0], parts[1]); 85 } 86 } 87 88 static class CompoundKeySerializer extends JsonSerializer<CompoundKey> { 89 @Override serialize(CompoundKey compoundKey, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)90 public void serialize(CompoundKey compoundKey, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { 91 jsonGenerator.writeFieldName(compoundKey.getPart0() + '|' + compoundKey.getPart1()); 92 } 93 } 94 95 /* 96 /******************************************************* 97 /* Test methods 98 /******************************************************* 99 */ 100 testMapRefinement()101 public void testMapRefinement() throws Exception 102 { 103 String ID1 = "3a6383d4-8123-4c43-8b8d-7cedf3e59404"; 104 String ID2 = "81c3d978-90c4-4b00-8da1-1c39ffcab02c"; 105 String json = aposToQuotes( 106 "{'id':'"+ID1+"','items':[{'id':'"+ID2+"','property':'value'}]}"); 107 108 ObjectMapper m = new ObjectMapper(); 109 Data data = m.readValue(json, Data.class); 110 111 assertEquals(ID1, data.id); 112 assertNotNull(data.items); 113 assertEquals(1, data.items.size()); 114 Item value = data.items.get(ID2); 115 assertNotNull(value); 116 assertEquals("value", value.property); 117 } 118 119 // for [databind#1384] testMapKeyRefinement1384()120 public void testMapKeyRefinement1384() throws Exception 121 { 122 final String TEST_INSTANCE_SERIALIZED = 123 "{\"mapProperty\":[\"java.util.HashMap\",{\"Compound|Key\":\"Value\"}]}"; 124 ObjectMapper mapper = jsonMapperBuilder() 125 .activateDefaultTyping(NoCheckSubTypeValidator.instance, 126 ObjectMapper.DefaultTyping.NON_FINAL) 127 .build(); 128 129 TestClass testInstance = mapper.readValue(TEST_INSTANCE_SERIALIZED, TestClass.class); 130 assertEquals(1, testInstance.mapProperty.size()); 131 Object key = testInstance.mapProperty.keySet().iterator().next(); 132 assertEquals(CompoundKey.class, key.getClass()); 133 String testInstanceSerialized = mapper.writeValueAsString(testInstance); 134 assertEquals(TEST_INSTANCE_SERIALIZED, testInstanceSerialized); 135 } 136 } 137