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