1 package com.fasterxml.jackson.databind.deser; 2 3 import java.io.IOException; 4 import java.util.Arrays; 5 import java.util.HashMap; 6 import java.util.List; 7 import java.util.Map; 8 9 import com.fasterxml.jackson.annotation.JsonAnySetter; 10 import com.fasterxml.jackson.annotation.JsonSubTypes; 11 import com.fasterxml.jackson.annotation.JsonSubTypes.Type; 12 import com.fasterxml.jackson.annotation.JsonTypeInfo; 13 import com.fasterxml.jackson.core.*; 14 import com.fasterxml.jackson.databind.*; 15 import com.fasterxml.jackson.databind.module.SimpleModule; 16 17 public class NullHandlingTest extends BaseMapTest 18 { 19 static class FunnyNullDeserializer extends JsonDeserializer<String> 20 { 21 @Override deserialize(JsonParser jp, DeserializationContext ctxt)22 public String deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { 23 return "text"; 24 } 25 26 @Override getNullValue(DeserializationContext ctxt)27 public String getNullValue(DeserializationContext ctxt) { return "funny"; } 28 } 29 30 static class AnySetter{ 31 32 private Map<String,String> any = new HashMap<String,String>(); 33 34 @JsonAnySetter setAny(String name, String value)35 public void setAny(String name, String value){ 36 this.any.put(name,value); 37 } 38 getAny()39 public Map<String,String> getAny(){ 40 return this.any; 41 } 42 } 43 44 // [databind#1601] 45 static class RootData { 46 public String name; 47 public String type; 48 @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, 49 property = "type") 50 @JsonSubTypes({ 51 @Type(value = TypeA.class, name = "TypeA"), 52 @Type(value = TypeB.class, name = "TypeB")}) 53 public Proxy proxy; 54 RootData()55 public RootData() {} 56 RootData(String name, String type, Proxy proxy)57 public RootData(String name, String type, Proxy proxy) { 58 this.name = name; 59 this.type = type; 60 this.proxy = proxy; 61 } 62 } 63 static interface Proxy { } 64 65 static class TypeA implements Proxy { 66 public String aValue; TypeA()67 public TypeA() {} TypeA(String a)68 public TypeA(String a) { 69 this.aValue = a; 70 } 71 } 72 73 static class TypeB implements Proxy { 74 public String bValue; TypeB()75 public TypeB() {} TypeB(String b)76 public TypeB(String b) { 77 this.bValue = b; 78 } 79 } 80 81 private final ObjectMapper MAPPER = objectMapper(); 82 83 /* 84 /********************************************************** 85 /* Test methods 86 /********************************************************** 87 */ 88 testNull()89 public void testNull() throws Exception 90 { 91 // null doesn't really have a type, fake by assuming Object 92 Object result = MAPPER.readValue(" null", Object.class); 93 assertNull(result); 94 } 95 testAnySetterNulls()96 public void testAnySetterNulls() throws Exception { 97 ObjectMapper mapper = new ObjectMapper(); 98 SimpleModule module = new SimpleModule("test", Version.unknownVersion()); 99 module.addDeserializer(String.class, new FunnyNullDeserializer()); 100 mapper.registerModule(module); 101 102 String fieldName = "fieldName"; 103 String nullValue = "{\""+fieldName+"\":null}"; 104 105 // should get non-default null directly: 106 AnySetter result = mapper.readValue(nullValue, AnySetter.class); 107 108 assertEquals(1, result.getAny().size()); 109 assertNotNull(result.getAny().get(fieldName)); 110 assertEquals("funny", result.getAny().get(fieldName)); 111 112 // as well as via ObjectReader 113 ObjectReader reader = mapper.readerFor(AnySetter.class); 114 result = reader.readValue(nullValue); 115 116 assertEquals(1, result.getAny().size()); 117 assertNotNull(result.getAny().get(fieldName)); 118 assertEquals("funny", result.getAny().get(fieldName)); 119 } 120 testCustomRootNulls()121 public void testCustomRootNulls() throws Exception 122 { 123 ObjectMapper mapper = new ObjectMapper(); 124 SimpleModule module = new SimpleModule("test", Version.unknownVersion()); 125 module.addDeserializer(String.class, new FunnyNullDeserializer()); 126 mapper.registerModule(module); 127 128 // should get non-default null directly: 129 String str = mapper.readValue("null", String.class); 130 assertNotNull(str); 131 assertEquals("funny", str); 132 133 // as well as via ObjectReader 134 ObjectReader reader = mapper.readerFor(String.class); 135 str = reader.readValue("null"); 136 assertNotNull(str); 137 assertEquals("funny", str); 138 } 139 140 // [databind#407] testListOfNulls()141 public void testListOfNulls() throws Exception 142 { 143 ObjectMapper mapper = new ObjectMapper(); 144 SimpleModule module = new SimpleModule("test", Version.unknownVersion()); 145 module.addDeserializer(String.class, new FunnyNullDeserializer()); 146 mapper.registerModule(module); 147 148 List<String> list = Arrays.asList("funny"); 149 JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, String.class); 150 151 // should get non-default null directly: 152 List<?> deser = mapper.readValue("[null]", type); 153 assertNotNull(deser); 154 assertEquals(1, deser.size()); 155 assertEquals(list.get(0), deser.get(0)); 156 157 // as well as via ObjectReader 158 ObjectReader reader = mapper.readerFor(type); 159 deser = reader.readValue("[null]"); 160 assertNotNull(deser); 161 assertEquals(1, deser.size()); 162 assertEquals(list.get(0), deser.get(0)); 163 } 164 165 // Test for [#407] testMapOfNulls()166 public void testMapOfNulls() throws Exception 167 { 168 ObjectMapper mapper = new ObjectMapper(); 169 SimpleModule module = new SimpleModule("test", Version.unknownVersion()); 170 module.addDeserializer(String.class, new FunnyNullDeserializer()); 171 mapper.registerModule(module); 172 173 JavaType type = mapper.getTypeFactory().constructMapType(Map.class, String.class, String.class); 174 // should get non-default null directly: 175 Map<?,?> deser = mapper.readValue("{\"key\":null}", type); 176 assertNotNull(deser); 177 assertEquals(1, deser.size()); 178 assertEquals("funny", deser.get("key")); 179 180 // as well as via ObjectReader 181 ObjectReader reader = mapper.readerFor(type); 182 deser = reader.readValue("{\"key\":null}"); 183 assertNotNull(deser); 184 assertEquals(1, deser.size()); 185 assertEquals("funny", deser.get("key")); 186 } 187 188 // [databind#1601] testPolymorphicDataNull()189 public void testPolymorphicDataNull() throws Exception 190 { 191 String typeA = 192 "{\"name\":\"TypeAData\", \"type\":\"TypeA\", \"proxy\":{\"aValue\":\"This works!\"}}"; 193 RootData typeAData = MAPPER.readValue(typeA, RootData.class); 194 assertEquals("No value for aValue!?", "This works!", ((TypeA) typeAData.proxy).aValue); 195 String typeB = 196 "{\"name\":\"TypeBData\", \"type\":\"TypeB\", \"proxy\":{\"bValue\":\"This works too!\"}}"; 197 RootData typeBData = MAPPER.readValue(typeB, RootData.class); 198 assertEquals("No value for bValue!?", "This works too!", ((TypeB) typeBData.proxy).bValue); 199 String typeBNull = 200 "{\"name\":\"TypeBData\", \"type\":\"TypeB\", \"proxy\": null}"; 201 RootData typeBNullData = MAPPER.readValue(typeBNull, RootData.class); 202 assertNull("Proxy should be null!", typeBNullData.proxy); 203 } 204 } 205