SerializationAnnotationsTest.java
package tools.jackson.databind.ser;
import java.io.*;
import java.util.*;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.*;
import tools.jackson.core.*;
import tools.jackson.databind.*;
import tools.jackson.databind.annotation.JsonSerialize;
import tools.jackson.databind.ser.std.StdSerializer;
import tools.jackson.databind.testutil.DatabindTestUtil;
import static org.junit.jupiter.api.Assertions.*;
/**
* This unit test suite tests use of some basic Jackson annotations for
* bean serialization, as well as serialization ordering constraints.
*/
public class SerializationAnnotationsTest
extends DatabindTestUtil
{
// Class for testing {@link JsonProperty} annotations with getters
final static class SizeClassGetter
{
@JsonProperty public int size() { return 3; }
@JsonProperty("length") public int foobar() { return -17; }
// note: need not be public since there's annotation
@JsonProperty protected int value() { return 0; }
// dummy method; not a getter signature
protected int getNotReally(int arg) { return 0; }
}
// And additional testing to cover [JACKSON-64]
final static class SizeClassGetter2
{
// Should still be considered property "x"
@JsonProperty protected int getX() { return 3; }
}
// and some support for testing [JACKSON-120]
final static class SizeClassGetter3
{
// Should be considered property "y" even tho non-public
@JsonSerialize protected int getY() { return 8; }
}
/**
* Class for testing {@link ValueSerializer} annotation
* for class itself.
*/
@JsonSerialize(using=BogusSerializer.class)
final static class ClassSerializer {
}
/**
* Class for testing an active {@link JsonSerialize#using} annotation
* for a method
*/
final static class ClassMethodSerializer {
private int _x;
public ClassMethodSerializer(int x) { _x = x; }
@JsonSerialize(using=StringSerializer.class)
public int getX() { return _x; }
}
/**
* Class for testing an inactive (one that will not have any effect)
* {@link JsonSerialize} annotation for a method
*/
final static class InactiveClassMethodSerializer {
private int _x;
public InactiveClassMethodSerializer(int x) { _x = x; }
// Basically, has no effect, hence gets serialized as number
@JsonSerialize(using=ValueSerializer.None.class)
public int getX() { return _x; }
}
/**
* Class for verifying that getter information is inherited
* as expected via normal class inheritance
*/
static class BaseBean {
public int getX() { return 1; }
@JsonProperty("y")
private int getY() { return 2; }
}
static class SubClassBean extends BaseBean {
public int getZ() { return 3; }
}
// Base class for testing {@link JsonProperty} annotations
static class BasePojo
{
@JsonProperty public int width() { return 3; }
@JsonProperty public int length() { return 7; }
}
interface PojoInterface
{
@JsonProperty int width();
@JsonProperty int length();
}
static class PojoSubclass extends BasePojo
{
@Override
public int width() { return 9; }
}
static class PojoImpl implements PojoInterface
{
@Override
public int width() { return 1; }
@Override
public int length() { return 2; }
public int getFoobar() { return 5; }
}
/*
/**********************************************************
/* Other helper classes (annotations test)
/**********************************************************
*/
public final static class BogusSerializer extends StdSerializer<Object>
{
public BogusSerializer() { super(Object.class); }
@Override
public void serialize(Object value, JsonGenerator g, SerializationContext provider)
{
g.writeBoolean(true);
}
}
private final static class StringSerializer extends StdSerializer<Object>
{
public StringSerializer() { super(Object.class); }
@Override
public void serialize(Object value, JsonGenerator g, SerializationContext provider)
{
g.writeString("X"+value+"X");
}
}
// From SerializationOrderTest
static class BeanWithCreator
{
public int a;
public int b;
public int c;
@JsonCreator
public BeanWithCreator(@JsonProperty("c") int c, @JsonProperty("a") int a) {
this.a = a;
this.c = c;
}
}
@JsonPropertyOrder({"c", "a", "b"})
static class BeanWithOrder
{
public int d, b, a, c;
public BeanWithOrder(int a, int b, int c, int d) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
}
@JsonPropertyOrder(value={"d"}, alphabetic=true)
static class SubBeanWithOrder extends BeanWithOrder
{
public SubBeanWithOrder(int a, int b, int c, int d) {
super(a, b, c, d);
}
}
@JsonPropertyOrder({"b", "a",
// note: including non-existant properties is fine (has no effect, but not an error)
"foobar",
"c"
})
static class OrderMixIn { }
@JsonPropertyOrder(value={"a","b","x","z"})
static class BeanFor268 {
@JsonProperty("a") public String xA = "a";
@JsonProperty("z") public String aZ = "z";
@JsonProperty("b") public String xB() { return "b"; }
@JsonProperty("x") public String aX() { return "x"; }
}
static class BeanFor459 {
public int d = 4;
public int c = 3;
public int b = 2;
public int a = 1;
}
// For [databind#311]
@JsonPropertyOrder(alphabetic = true)
static class BeanForGH311 {
private final int a;
private final int b;
@JsonCreator
public BeanForGH311(@JsonProperty("b") int b, @JsonProperty("a") int a) {
this.a = a;
this.b = b;
}
public int getA() { return a; }
public int getB() { return b; }
}
// We'll expect ordering of "FUBAR"
@JsonPropertyOrder({ "f" })
static class OrderingByIndexBean {
public int r;
public int a;
@JsonProperty(index = 1)
public int b;
@JsonProperty(index = 0)
public int u;
public int f;
}
// For [databind#2879]
@JsonPropertyOrder({ "a", "c" })
static class BeanFor2879 {
public int c;
public int b;
public int a;
@JsonCreator
public BeanFor2879(@JsonProperty("a") int a,
@JsonProperty("b") int b,
@JsonProperty("c") int c) {
this.a = a;
this.b = b;
this.c = c;
}
}
// For [databind#2879]
static class BeanForStrictOrdering {
private final int a;
private int b;
private final int c;
@JsonCreator
public BeanForStrictOrdering(@JsonProperty("c") int c, @JsonProperty("a") int a) {
this.a = a;
this.c = c;
}
public int getA() { return a; }
public int getB() { return b; }
public int getC() { return c; }
}
/*
/**********************************************************
/* Main tests
/**********************************************************
*/
private final ObjectMapper MAPPER = newJsonMapper();
private final ObjectMapper ALPHA_MAPPER = jsonMapperBuilder()
.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
.build();
@Test
public void testSimpleGetter() throws Exception
{
Map<String,Object> result = writeAndMap(MAPPER, new SizeClassGetter());
assertEquals(3, result.size());
assertEquals(Integer.valueOf(3), result.get("size"));
assertEquals(Integer.valueOf(-17), result.get("length"));
assertEquals(Integer.valueOf(0), result.get("value"));
}
@Test
public void testSimpleGetter2() throws Exception
{
Map<String,Object> result = writeAndMap(MAPPER, new SizeClassGetter2());
assertEquals(1, result.size());
assertEquals(Integer.valueOf(3), result.get("x"));
}
@Test
public void testSimpleGetter3() throws Exception
{
Map<String,Object> result = writeAndMap(MAPPER, new SizeClassGetter3());
assertEquals(1, result.size());
assertEquals(Integer.valueOf(8), result.get("y"));
}
@Test
public void testGetterInheritance() throws Exception
{
Map<String,Object> result = writeAndMap(MAPPER, new SubClassBean());
assertEquals(3, result.size());
assertEquals(Integer.valueOf(1), result.get("x"));
assertEquals(Integer.valueOf(2), result.get("y"));
assertEquals(Integer.valueOf(3), result.get("z"));
}
@Test
public void testSimpleGetterInheritance() throws Exception
{
Map<String,Object> result = writeAndMap(MAPPER, new PojoSubclass());
assertEquals(2, result.size());
assertEquals(Integer.valueOf(7), result.get("length"));
assertEquals(Integer.valueOf(9), result.get("width"));
}
@Test
public void testSimpleGetterInterfaceImpl() throws Exception
{
Map<String,Object> result = writeAndMap(MAPPER, new PojoImpl());
// should get 2 from interface, and one more from impl itself
assertEquals(3, result.size());
assertEquals(Integer.valueOf(5), result.get("foobar"));
assertEquals(Integer.valueOf(1), result.get("width"));
assertEquals(Integer.valueOf(2), result.get("length"));
}
@Test
public void testClassSerializer() throws Exception
{
StringWriter sw = new StringWriter();
MAPPER.writeValue(sw, new ClassSerializer());
assertEquals("true", sw.toString());
}
@Test
public void testActiveMethodSerializer() throws Exception
{
StringWriter sw = new StringWriter();
MAPPER.writeValue(sw, new ClassMethodSerializer(13));
assertEquals("{\"x\":\"X13X\"}", sw.toString());
}
@Test
public void testInactiveMethodSerializer() throws Exception
{
String json = MAPPER.writeValueAsString(new InactiveClassMethodSerializer(8));
assertEquals("{\"x\":8}", json);
}
// From SerializationOrderTest
@Test
public void testImplicitOrderByCreator() throws Exception {
assertEquals("{\"c\":1,\"a\":2,\"b\":0}",
MAPPER.writeValueAsString(new BeanWithCreator(1, 2)));
}
@Test
public void testExplicitOrder() throws Exception {
assertEquals("{\"c\":3,\"a\":1,\"b\":2,\"d\":4}",
MAPPER.writeValueAsString(new BeanWithOrder(1, 2, 3, 4)));
}
@Test
public void testAlphabeticOrder() throws Exception {
assertEquals("{\"d\":4,\"a\":1,\"b\":2,\"c\":3}",
MAPPER.writeValueAsString(new SubBeanWithOrder(1, 2, 3, 4)));
}
@Test
public void testOrderWithMixins() throws Exception
{
ObjectMapper mapper = jsonMapperBuilder()
.addMixIn(BeanWithOrder.class, OrderMixIn.class)
.build();
assertEquals("{\"b\":2,\"a\":1,\"c\":3,\"d\":4}",
mapper.writeValueAsString(new BeanWithOrder(1, 2, 3, 4)));
}
@Test
public void testOrderWrt268() throws Exception
{
assertEquals("{\"a\":\"a\",\"b\":\"b\",\"x\":\"x\",\"z\":\"z\"}",
MAPPER.writeValueAsString(new BeanFor268()));
}
@Test
public void testOrderWithFeature() throws Exception
{
assertEquals("{\"a\":1,\"b\":2,\"c\":3,\"d\":4}",
ALPHA_MAPPER.writeValueAsString(new BeanFor459()));
}
// [databind#2879]: verify that Creator properties never override explicit order
@Test
public void testCreatorVsExplicitOrdering() throws Exception
{
assertEquals(a2q("{'a':1,'c':3,'b':2}"),
MAPPER.writeValueAsString(new BeanFor2879(1, 2, 3)));
assertEquals(a2q("{'a':1,'c':3,'b':2}"),
ALPHA_MAPPER.writeValueAsString(new BeanFor2879(1, 2, 3)));
}
// [databind#311]
@Test
public void testAlphaAndCreatorOrdering() throws Exception
{
assertEquals(a2q("{'b':2,'a':1}"),
ALPHA_MAPPER.writeValueAsString(new BeanForGH311(2, 1)));
final ObjectMapper mapper = jsonMapperBuilder()
.disable(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST)
.build();
assertEquals(a2q("{'a':1,'b':2}"),
mapper.writeValueAsString(new BeanForGH311(2, 1)));
}
// [databind#2555]
@Test
public void testOrderByIndexEtc() throws Exception
{
// since "default" order can actually vary with later JDKs, only verify
// case of alphabetic-as-default
assertEquals(a2q("{'f':0,'u':0,'b':0,'a':0,'r':0}"),
ALPHA_MAPPER.writeValueAsString(new OrderingByIndexBean()));
}
// [databind#2879]: allow preventing Creator properties from overriding alphabetic ordering
@Test
public void testStrictAlphaAndCreatorOrdering() throws Exception
{
// without changing defaults, creators are sorted before other properties
assertTrue(ALPHA_MAPPER.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));
assertTrue(ALPHA_MAPPER.isEnabled(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST));
assertEquals(a2q("{'c':2,'a':3,'b':0}"),
ALPHA_MAPPER.writeValueAsString(new BeanForStrictOrdering(2, 3)));
// but can change that
final ObjectMapper STRICT_ALPHA_MAPPER = jsonMapperBuilder()
.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
.disable(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST)
.build();
assertEquals(a2q("{'a':2,'b':0,'c':1}"),
STRICT_ALPHA_MAPPER.writeValueAsString(new BeanForStrictOrdering(1, 2)));
}
}