blob: 0282a652f3f0d929bf2700a5ce8ca4a3dd95ba3e [file] [log] [blame]
package org.codehaus.jackson.map.module;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.*;
import org.codehaus.jackson.*;
import org.codehaus.jackson.annotate.JsonPropertyOrder;
import org.codehaus.jackson.map.*;
import org.codehaus.jackson.map.ser.std.ScalarSerializerBase;
import org.codehaus.jackson.map.ser.std.SerializerBase;
public class TestSimpleModule extends BaseMapTest
{
/*
/**********************************************************
/* Helper classes; simple beans and their handlers
/**********************************************************
*/
/**
* Trivial bean that requires custom serializer and deserializer
*/
final static class CustomBean
{
protected String str;
protected int num;
public CustomBean(String s, int i) {
str = s;
num = i;
}
}
static enum SimpleEnum { A, B; }
// Extend SerializerBase to get access to declared handledType
static class CustomBeanSerializer extends SerializerBase<CustomBean>
{
public CustomBeanSerializer() { super(CustomBean.class); }
@Override
public void serialize(CustomBean value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException
{
// We will write it as a String, with '|' as delimiter
jgen.writeString(value.str + "|" + value.num);
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException {
return null;
}
}
static class CustomBeanDeserializer extends JsonDeserializer<CustomBean>
{
@Override
public CustomBean deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
String text = jp.getText();
int ix = text.indexOf('|');
if (ix < 0) {
throw new IOException("Failed to parse String value of \""+text+"\"");
}
String str = text.substring(0, ix);
int num = Integer.parseInt(text.substring(ix+1));
return new CustomBean(str, num);
}
}
static class SimpleEnumSerializer extends SerializerBase<SimpleEnum>
{
public SimpleEnumSerializer() { super(SimpleEnum.class); }
@Override
public void serialize(SimpleEnum value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException
{
jgen.writeString(value.name().toLowerCase());
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException {
return null;
}
}
static class SimpleEnumDeserializer extends JsonDeserializer<SimpleEnum>
{
@Override
public SimpleEnum deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
return SimpleEnum.valueOf(jp.getText().toUpperCase());
}
}
interface Base {
public String getText();
}
static class Impl1 implements Base {
@Override
public String getText() { return "1"; }
}
static class Impl2 extends Impl1 {
@Override
public String getText() { return "2"; }
}
static class BaseSerializer extends ScalarSerializerBase<Base>
{
public BaseSerializer() { super(Base.class); }
@Override
public void serialize(Base value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeString("Base:"+value.getText());
}
}
static class MixableBean {
public int a = 1;
public int b = 2;
public int c = 3;
}
@JsonPropertyOrder({"c", "a", "b"})
static class MixInForOrder { }
/*
/**********************************************************
/* Unit tests; first, verifying need for custom handlers
/**********************************************************
*/
/**
* Basic test to ensure we do not have functioning default
* serializers for custom types used in tests.
*/
public void testWithoutModule()
{
ObjectMapper mapper = new ObjectMapper();
// first: serialization failure:
try {
mapper.writeValueAsString(new CustomBean("foo", 3));
fail("Should have caused an exception");
} catch (IOException e) {
verifyException(e, "No serializer found");
}
// then deserialization
try {
mapper.readValue("{\"str\":\"ab\",\"num\":2}", CustomBean.class);
fail("Should have caused an exception");
} catch (IOException e) {
verifyException(e, "No suitable constructor found");
}
}
protected static class MySimpleSerializers extends SimpleSerializers { }
protected static class MySimpleDeserializers extends SimpleDeserializers { }
/**
* Test module which uses custom 'serializers' and 'deserializers' container; used
* to trigger type problems.
*/
protected static class MySimpleModule extends SimpleModule
{
public MySimpleModule(String name, Version version) {
super(name, version);
_deserializers = new MySimpleDeserializers();
_serializers = new MySimpleSerializers();
}
}
/*
/**********************************************************
/* Unit tests; simple serializers
/**********************************************************
*/
public void testSimpleBeanSerializer() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
SimpleModule mod = new SimpleModule("test", new Version(1, 0, 0, null));
mod.addSerializer(new CustomBeanSerializer());
mapper.registerModule(mod);
assertEquals(quote("abcde|5"), mapper.writeValueAsString(new CustomBean("abcde", 5)));
}
public void testSimpleEnumSerializer() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
SimpleModule mod = new SimpleModule("test", new Version(1, 0, 0, null));
mod.addSerializer(new SimpleEnumSerializer());
mapper.registerModule(mod);
assertEquals(quote("b"), mapper.writeValueAsString(SimpleEnum.B));
}
// for [JACKSON-550]
public void testSimpleInterfaceSerializer() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
SimpleModule mod = new SimpleModule("test", new Version(1, 0, 0, null));
mod.addSerializer(new BaseSerializer());
mapper.registerModule(mod);
assertEquals(quote("Base:1"), mapper.writeValueAsString(new Impl1()));
assertEquals(quote("Base:2"), mapper.writeValueAsString(new Impl2()));
}
/*
/**********************************************************
/* Unit tests; simple deserializers
/**********************************************************
*/
public void testSimpleBeanDeserializer() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
SimpleModule mod = new SimpleModule("test", new Version(1, 0, 0, null));
mod.addDeserializer(CustomBean.class, new CustomBeanDeserializer());
mapper.registerModule(mod);
CustomBean bean = mapper.readValue(quote("xyz|3"), CustomBean.class);
assertEquals("xyz", bean.str);
assertEquals(3, bean.num);
}
public void testSimpleEnumDeserializer() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
SimpleModule mod = new SimpleModule("test", new Version(1, 0, 0, null));
mod.addDeserializer(SimpleEnum.class, new SimpleEnumDeserializer());
mapper.registerModule(mod);
SimpleEnum result = mapper.readValue(quote("a"), SimpleEnum.class);
assertSame(SimpleEnum.A, result);
}
// Simple verification of [JACKSON-455]
public void testMultipleModules() throws Exception
{
MySimpleModule mod1 = new MySimpleModule("test1", new Version(1, 0, 0, null));
SimpleModule mod2 = new SimpleModule("test2", new Version(1, 0, 0, null));
mod1.addSerializer(SimpleEnum.class, new SimpleEnumSerializer());
mod1.addDeserializer(CustomBean.class, new CustomBeanDeserializer());
mod2.addDeserializer(SimpleEnum.class, new SimpleEnumDeserializer());
mod2.addSerializer(CustomBean.class, new CustomBeanSerializer());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(mod1);
mapper.registerModule(mod2);
assertEquals(quote("b"), mapper.writeValueAsString(SimpleEnum.B));
SimpleEnum result = mapper.readValue(quote("a"), SimpleEnum.class);
assertSame(SimpleEnum.A, result);
// also let's try it with different order of registration, just in case
mapper = new ObjectMapper();
mapper.registerModule(mod2);
mapper.registerModule(mod1);
assertEquals(quote("b"), mapper.writeValueAsString(SimpleEnum.B));
result = mapper.readValue(quote("a"), SimpleEnum.class);
assertSame(SimpleEnum.A, result);
}
/*
/**********************************************************
/* Unit tests; other
/**********************************************************
*/
// [JACKSON-644]: ability to register mix-ins
public void testMixIns() throws Exception
{
SimpleModule module = new SimpleModule("test", Version.unknownVersion());
module.setMixInAnnotation(MixableBean.class, MixInForOrder.class);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
Map<String,Object> props = this.writeAndMap(mapper, new MixableBean());
assertEquals(3, props.size());
assertEquals(Integer.valueOf(3), props.get("c"));
assertEquals(Integer.valueOf(1), props.get("a"));
assertEquals(Integer.valueOf(2), props.get("b"));
}
}