SimpleModuleAddMethodsTest.java
package com.fasterxml.jackson.databind.module;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.fasterxml.jackson.databind.testutil.DatabindTestUtil;
import static org.junit.jupiter.api.Assertions.*;
/**
* Behavioral test to prove that by design decision, (de)serializers from class-level annotations will always
* have preference over default (de)serializers.
*
*/
@SuppressWarnings("serial")
public class SimpleModuleAddMethodsTest extends DatabindTestUtil
{
@JsonDeserialize(using = ClassDogDeserializer.class)
@JsonSerialize(using = ClassDogSerializer.class)
static class Dog {
public String name;
public Dog(String name) {
this.name = name;
}
}
@JsonDeserialize(keyUsing = ClassDogKeyDeserializer.class, contentUsing = ClassDogDeserializer.class)
@JsonSerialize(keyUsing = ClassDogKeySerializer.class, contentUsing = ClassDogSerializer.class)
static class DogMap extends HashMap<Dog, Dog> {}
@JsonDeserialize(contentUsing = ClassDogDeserializer.class)
@JsonSerialize(contentUsing = ClassDogSerializer.class)
static class DogList extends ArrayList<Dog> {}
static class ClassDogSerializer extends JsonSerializer<Dog> {
@Override
public void serialize(Dog value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString("class-dog");
}
}
static class ClassDogDeserializer extends JsonDeserializer<Dog> {
@Override
public Dog deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
p.skipChildren();
return new Dog("class-dog");
}
}
static class ClassDogKeySerializer extends JsonSerializer<Dog> {
@Override
public void serialize(Dog value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeFieldName("class-dog");
}
}
static class ClassDogKeyDeserializer extends KeyDeserializer {
@Override
public Object deserializeKey(String key, DeserializationContext ctxt) {
return new Dog("class-dog");
}
}
static class ModuleDogDeserializer extends JsonDeserializer<Dog> {
@Override
public Dog deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
p.skipChildren();
return new Dog("module-dog");
}
}
static class ModuleDogSerializer extends JsonSerializer<Dog> {
@Override
public void serialize(Dog value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString("module-dog");
}
}
static class ModuleDogKeyDeserializer extends KeyDeserializer {
@Override
public Object deserializeKey(String key, DeserializationContext ctxt) {
return new Dog("module-dog");
}
}
static class ModuleDogKeySerializer extends JsonSerializer<Dog> {
@Override
public void serialize(Dog value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeFieldName("module-dog");
}
}
@JsonDeserialize(builder = SimpleBuilderXY.class)
static class BuildFailBean {
final int _x, _y;
protected BuildFailBean(int x, int y) {
_x = x;
_y = y;
}
}
@JsonDeserialize(builder = SimpleBuilderXY.class)
static class BuildSuccessBean {
final int _x, _y;
protected BuildSuccessBean(int x, int y) {
_x = x;
_y = y;
}
}
@JsonDeserialize(builder = java.lang.Void.class)
public static abstract class BuildBeanMixin {}
static class SimpleBuilderXY {
public int x, y;
public SimpleBuilderXY withX(int x0) {
this.x = x0;
return this;
}
public SimpleBuilderXY withY(int y0) {
this.y = y0;
return this;
}
public BuildFailBean build() {
return new BuildFailBean(x, y);
}
}
static class BuildSuccessBeanDeserializer extends JsonDeserializer<BuildSuccessBean> {
@Override
public BuildSuccessBean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
p.skipChildren();
return new BuildSuccessBean(7, 8);
}
}
/*
/**********************************************************
/* Test methods
/**********************************************************
*/
private final ObjectMapper MAPPER = _buildMapper();
private ObjectMapper _buildMapper() {
ObjectMapper mapper = newJsonMapper();
// Simple tests
SimpleModule simpleModule = new SimpleModule()
.addSerializer(Dog.class, new ModuleDogSerializer())
.addDeserializer(Dog.class, new ModuleDogDeserializer())
.addKeyDeserializer(Dog.class, new ModuleDogKeyDeserializer())
.addKeySerializer(Dog.class, new ModuleDogKeySerializer());
// "remove" builder annotation using mix-in
mapper.addMixIn(BuildFailBean.class, BuildBeanMixin.class);
// "remove" builder annotation using mix-in, then register deserializer using module
mapper.addMixIn(BuildSuccessBean.class, BuildBeanMixin.class);
simpleModule.addDeserializer(BuildSuccessBean.class, new BuildSuccessBeanDeserializer());
mapper.registerModule(simpleModule);
return mapper;
}
@Test
public void testPojoDeserialization() throws Exception {
Dog dog = MAPPER.readValue(a2q("{'name': 'my-dog'}"), Dog.class);
assertEquals("class-dog", dog.name);
}
@Test
public void testPojoSerialization() throws Exception {
assertEquals(
a2q("'class-dog'"),
MAPPER.writeValueAsString(new Dog("my-dog")));
}
@Test
public void testRemoveAnnotationUsingMixIn() throws Exception {
try {
MAPPER.readValue(
a2q("{'x':1, 'y':2}"), BuildFailBean.class);
fail("Should not pass");
} catch (InvalidDefinitionException e) {
verifyException(e, "cannot deserialize from Object value (no delegate- or property-based Creator)");
}
}
@Test
public void testRemoveAnnotationUsingMixInAndOverrideByModule() throws Exception {
BuildSuccessBean bean = MAPPER.readValue(
a2q("{'x':1, 'y':2}"), BuildSuccessBean.class);
assertEquals(7, bean._x);
assertEquals(8, bean._y);
}
@Test
public void testDogMapDeserialization() throws Exception {
DogMap map = MAPPER.readValue(a2q("{'simple-dog': 'simple-dog'}"), DogMap.class);
assertEquals(1, map.size());
for (Map.Entry<Dog, Dog> entry : map.entrySet()) {
assertEquals("class-dog", entry.getKey().name);
assertEquals("class-dog", entry.getValue().name);
}
}
@Test
public void testDogMapSerialization() throws Exception {
DogMap map = new DogMap();
map.put(new Dog("my-dog"), new Dog("my-dog"));
assertEquals(
a2q("{'class-dog':'class-dog'}"),
MAPPER.writeValueAsString(map));
}
@Test
public void testDogListDeserialization() throws Exception {
DogList list = MAPPER.readValue(a2q("['simple-dog']"), DogList.class);
assertEquals(1, list.size());
for (Dog dog : list) {
assertEquals("class-dog", dog.name);
}
}
@Test
public void testDogListSerialization() throws Exception {
DogList list = new DogList();
list.add(new Dog("my-dog"));
assertEquals(
a2q("['class-dog']"),
MAPPER.writeValueAsString(list));
}
}