blob: 9efa34173489d3fae5ec8815dae75bc64d7e8d32 [file] [log] [blame]
package org.codehaus.jackson.map.jsontype;
import java.util.*;
import org.codehaus.jackson.annotate.JsonTypeInfo;
import org.codehaus.jackson.annotate.JsonTypeName;
import org.codehaus.jackson.map.*;
import org.codehaus.jackson.type.TypeReference;
import static org.codehaus.jackson.annotate.JsonTypeInfo.*;
public class TestTypedSerialization
extends BaseMapTest
{
/*
/**********************************************************
/* Helper types
/**********************************************************
*/
/**
* Polymorphic base class
*/
@JsonTypeInfo(use=Id.CLASS, include=As.PROPERTY)
static abstract class Animal {
public String name;
protected Animal(String n) { name = n; }
}
@JsonTypeName("doggie")
static class Dog extends Animal
{
public int boneCount;
private Dog() { super(null); }
public Dog(String name, int b) {
super(name);
boneCount = b;
}
}
@JsonTypeName("kitty")
static class Cat extends Animal
{
public String furColor;
private Cat() { super(null); }
public Cat(String name, String c) {
super(name);
furColor = c;
}
}
public class AnimalWrapper {
public Animal animal;
public AnimalWrapper(Animal a) { animal = a; }
}
@JsonTypeInfo(use=Id.MINIMAL_CLASS, include=As.WRAPPER_OBJECT)
interface TypeWithWrapper { }
@JsonTypeInfo(use=Id.CLASS, include=As.WRAPPER_ARRAY)
interface TypeWithArray { }
@JsonTypeInfo(use=Id.NAME)
@JsonTypeName("empty")
public class Empty { }
@JsonTypeInfo(include=As.PROPERTY, use=Id.CLASS)
public class Super {}
public class A extends Super {}
public class B extends Super {}
/*
/**********************************************************
/* Unit tests
/**********************************************************
*/
/**
* First things first, let's ensure we can serialize using
* class name, written as main-level property name
*/
public void testSimpleClassAsProperty() throws Exception
{
Map<String,Object> result = writeAndMap(new Cat("Beelzebub", "tabby"));
assertEquals(3, result.size());
assertEquals("Beelzebub", result.get("name"));
assertEquals("tabby", result.get("furColor"));
// should we try customized class name?
String classProp = Id.CLASS.getDefaultPropertyName();
assertEquals(Cat.class.getName(), result.get(classProp));
}
/**
* Test inclusion using wrapper style
*/
public void testTypeAsWrapper() throws Exception
{
ObjectMapper m = new ObjectMapper();
m.getSerializationConfig().addMixInAnnotations(Animal.class, TypeWithWrapper.class);
Map<String,Object> result = writeAndMap(m, new Cat("Venla", "black"));
// should get a wrapper; keyed by minimal class name ("Cat" here)
assertEquals(1, result.size());
// minimal class name is prefixed by dot, and for inner classes it's bit longer
Map<?,?> cat = (Map<?,?>) result.get(".TestTypedSerialization$Cat");
assertNotNull(cat);
assertEquals(2, cat.size());
assertEquals("Venla", cat.get("name"));
assertEquals("black", cat.get("furColor"));
}
/**
* Test inclusion using 2-element array
*/
public void testTypeAsArray() throws Exception
{
ObjectMapper m = new ObjectMapper();
m.getSerializationConfig().addMixInAnnotations(Animal.class, TypeWithArray.class);
// hmmh. Not good idea to rely on exact output, order may change. But...
Map<String,Object> result = writeAndMap(m, new AnimalWrapper(new Dog("Amadeus", 7)));
// First level, wrapper
assertEquals(1, result.size());
List<?> l = (List<?>) result.get("animal");
assertNotNull(l);
assertEquals(2, l.size());
assertEquals(Dog.class.getName(), l.get(0));
Map<?,?> doggie = (Map<?,?>) l.get(1);
assertNotNull(doggie);
assertEquals(2, doggie.size());
assertEquals("Amadeus", doggie.get("name"));
assertEquals(Integer.valueOf(7), doggie.get("boneCount"));
}
/* !!! 30-Jan-2010, tatus: I am not completely sure below works as it should
* Problem is, context of "untyped" map should prevent type information
* being added to Animal entries, because Object.class has no type.
* If type information is included, it will not be useful for deserialization,
* since static type does not carry through (unlike in serialization).
*
* But it is not quite clear how type information should be pushed through
* array types...
*/
@SuppressWarnings("unchecked")
public void testInArray() throws Exception
{
// ensure we'll use mapper with default configs
ObjectMapper m = new ObjectMapper();
// ... so this should NOT be needed...
m.disableDefaultTyping();
Animal[] animals = new Animal[] { new Cat("Miuku", "white"), new Dog("Murre", 9) };
Map<String,Object> map = new HashMap<String,Object>();
map.put("a", animals);
String json = m.writeValueAsString(map);
Map<String,Object> result = m.readValue(json, Map.class);
assertEquals(1, result.size());
Object ob = result.get("a");
if (!(ob instanceof List<?>)) {
// 03-Feb-2010, tatu: Weird; seems to fail sometimes...
fail("Did not map to entry with 'a' as List (but as "+ob.getClass().getName()+"): JSON == '"+json+"'");
}
List<?> l = (List<?>)ob;
assertNotNull(l);
assertEquals(2, l.size());
Map<?,?> a1 = (Map<?,?>) l.get(0);
assertEquals(3, a1.size());
String classProp = Id.CLASS.getDefaultPropertyName();
assertEquals(Cat.class.getName(), a1.get(classProp));
Map<?,?> a2 = (Map<?,?>) l.get(1);
assertEquals(3, a2.size());
assertEquals(Dog.class.getName(), a2.get(classProp));
}
/**
* Simple unit test to verify that serializing "empty" beans is ok
*/
public void testEmptyBean() throws Exception
{
ObjectMapper m = new ObjectMapper();
m.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
assertEquals("{\"@type\":\"empty\"}", m.writeValueAsString(new Empty()));
}
/**
* Unit test for [JACKSON-543]
*/
public void testTypedMaps() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
Map<Long, Collection<Super>> map = new HashMap<Long, Collection<Super>>();
List<Super> list = new ArrayList<Super>();
list.add(new A());
map.put(1L, list);
String json = mapper.writerWithType(new TypeReference<Map<Long, Collection<Super>>>() {}).writeValueAsString(map);
assertTrue(json, json.contains("@class"));
}
}