package com.fasterxml.jackson.databind.deser;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class NullHandlingTest extends BaseMapTest
{
static class FunnyNullDeserializer extends JsonDeserializer<String>
{
@Override
public String deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
return "text";
}
@Override
public String getNullValue(DeserializationContext ctxt) { return "funny"; }
}
static class AnySetter{
private Map<String,String> any = new HashMap<String,String>();
@JsonAnySetter
public void setAny(String name, String value){
this.any.put(name,value);
}
public Map<String,String> getAny(){
return this.any;
}
}
// [databind#1601]
static class RootData {
public String name;
public String type;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
property = "type")
@JsonSubTypes({
@Type(value = TypeA.class, name = "TypeA"),
@Type(value = TypeB.class, name = "TypeB")})
public Proxy proxy;
public RootData() {}
public RootData(String name, String type, Proxy proxy) {
this.name = name;
this.type = type;
this.proxy = proxy;
}
}
static interface Proxy { }
static class TypeA implements Proxy {
public String aValue;
public TypeA() {}
public TypeA(String a) {
this.aValue = a;
}
}
static class TypeB implements Proxy {
public String bValue;
public TypeB() {}
public TypeB(String b) {
this.bValue = b;
}
}
private final ObjectMapper MAPPER = objectMapper();
/*
/**********************************************************
/* Test methods
/**********************************************************
*/
public void testNull() throws Exception
{
// null doesn't really have a type, fake by assuming Object
Object result = MAPPER.readValue(" null", Object.class);
assertNull(result);
}
public void testAnySetterNulls() throws Exception {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("test", Version.unknownVersion());
module.addDeserializer(String.class, new FunnyNullDeserializer());
mapper.registerModule(module);
String fieldName = "fieldName";
String nullValue = "{\""+fieldName+"\":null}";
// should get non-default null directly:
AnySetter result = mapper.readValue(nullValue, AnySetter.class);
assertEquals(1, result.getAny().size());
assertNotNull(result.getAny().get(fieldName));
assertEquals("funny", result.getAny().get(fieldName));
// as well as via ObjectReader
ObjectReader reader = mapper.readerFor(AnySetter.class);
result = reader.readValue(nullValue);
assertEquals(1, result.getAny().size());
assertNotNull(result.getAny().get(fieldName));
assertEquals("funny", result.getAny().get(fieldName));
}
public void testCustomRootNulls() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("test", Version.unknownVersion());
module.addDeserializer(String.class, new FunnyNullDeserializer());
mapper.registerModule(module);
// should get non-default null directly:
String str = mapper.readValue("null", String.class);
assertNotNull(str);
assertEquals("funny", str);
// as well as via ObjectReader
ObjectReader reader = mapper.readerFor(String.class);
str = reader.readValue("null");
assertNotNull(str);
assertEquals("funny", str);
}
// [databind#407]
public void testListOfNulls() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("test", Version.unknownVersion());
module.addDeserializer(String.class, new FunnyNullDeserializer());
mapper.registerModule(module);
List<String> list = Arrays.asList("funny");
JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, String.class);
// should get non-default null directly:
List<?> deser = mapper.readValue("[null]", type);
assertNotNull(deser);
assertEquals(1, deser.size());
assertEquals(list.get(0), deser.get(0));
// as well as via ObjectReader
ObjectReader reader = mapper.readerFor(type);
deser = reader.readValue("[null]");
assertNotNull(deser);
assertEquals(1, deser.size());
assertEquals(list.get(0), deser.get(0));
}
// Test for [#407]
public void testMapOfNulls() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("test", Version.unknownVersion());
module.addDeserializer(String.class, new FunnyNullDeserializer());
mapper.registerModule(module);
JavaType type = mapper.getTypeFactory().constructMapType(Map.class, String.class, String.class);
// should get non-default null directly:
Map<?,?> deser = mapper.readValue("{\"key\":null}", type);
assertNotNull(deser);
assertEquals(1, deser.size());
assertEquals("funny", deser.get("key"));
// as well as via ObjectReader
ObjectReader reader = mapper.readerFor(type);
deser = reader.readValue("{\"key\":null}");
assertNotNull(deser);
assertEquals(1, deser.size());
assertEquals("funny", deser.get("key"));
}
// [databind#1601]
public void testPolymorphicDataNull() throws Exception
{
String typeA =
"{\"name\":\"TypeAData\", \"type\":\"TypeA\", \"proxy\":{\"aValue\":\"This works!\"}}";
RootData typeAData = MAPPER.readValue(typeA, RootData.class);
assertEquals("No value for aValue!?", "This works!", ((TypeA) typeAData.proxy).aValue);
String typeB =
"{\"name\":\"TypeBData\", \"type\":\"TypeB\", \"proxy\":{\"bValue\":\"This works too!\"}}";
RootData typeBData = MAPPER.readValue(typeB, RootData.class);
assertEquals("No value for bValue!?", "This works too!", ((TypeB) typeBData.proxy).bValue);
String typeBNull =
"{\"name\":\"TypeBData\", \"type\":\"TypeB\", \"proxy\": null}";
RootData typeBNullData = MAPPER.readValue(typeBNull, RootData.class);
assertNull("Proxy should be null!", typeBNullData.proxy);
}
}