package com.fasterxml.jackson.databind.objectid;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIdentityReference;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.databind.*;
/**
* Unit test to verify handling of Object Id deserialization
*/
public class TestObjectIdSerialization extends BaseMapTest
{
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="id")
static class Identifiable
{
public int value;
public Identifiable next;
public Identifiable() { this(0); }
public Identifiable(int v) {
value = v;
}
}
@JsonIdentityInfo(generator=ObjectIdGenerators.StringIdGenerator.class, property="id")
static class StringIdentifiable
{
public int value;
public StringIdentifiable next;
public StringIdentifiable() { this(0); }
public StringIdentifiable(int v) {
value = v;
}
}
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="customId")
static class IdentifiableWithProp
{
public int value;
// Property that contains Object Id to use
public int customId;
public IdentifiableWithProp next;
public IdentifiableWithProp() { this(0, 0); }
public IdentifiableWithProp(int id, int value) {
this.customId = id;
this.value = value;
}
}
// For property reference, need another class:
static class IdWrapper
{
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
public ValueNode node;
public IdWrapper() { }
public IdWrapper(int v) {
node = new ValueNode(v);
}
}
static class ValueNode {
public int value;
public IdWrapper next;
public ValueNode() { this(0); }
public ValueNode(int v) { value = v; }
}
// Similarly for property-ref via property:
protected static class IdWrapperCustom
{
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
public ValueNodeCustom node;
public IdWrapperCustom() { }
public IdWrapperCustom(int id, int value) {
node = new ValueNodeCustom(id, value);
}
}
protected static class ValueNodeCustom {
public int value;
private int id;
public IdWrapperCustom next;
public int getId() { return id; }
public ValueNodeCustom() { this(0, 0); }
public ValueNodeCustom(int id, int value) {
this.id = id;
this.value = value;
}
}
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="id")
static class AlwaysAsId
{
public int value;
public AlwaysAsId() { this(0); }
public AlwaysAsId(int v) {
value = v;
}
}
// For [https://github.com/FasterXML/jackson-annotations/issues/4]
@JsonPropertyOrder(alphabetic=true)
static class AlwaysContainer
{
@JsonIdentityReference(alwaysAsId=true)
public AlwaysAsId a = new AlwaysAsId(13);
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="id")
@JsonIdentityReference(alwaysAsId=true)
public Value b = new Value();
}
static class Value {
public int x = 3;
}
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
static class TreeNode
{
public int id;
public String name;
@JsonIdentityReference(alwaysAsId=true)
public TreeNode parent;
// children serialized with ids if need be
public TreeNode child;
public TreeNode() { }
public TreeNode(TreeNode p, int id, String name) {
parent = p;
this.id = id;
this.name = name;
}
}
// // Let's also have one 'broken' test
// no "id" property
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
static class Broken
{
public int value;
public int customId;
}
// [databind#370]
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
public static class EmptyObject { }
//for [databind#1150]
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
static class IdentifiableStringId
{
public String id;
public int value;
public Identifiable next;
public IdentifiableStringId() { this(0); }
public IdentifiableStringId(int v) {
value = v;
}
}
/*
/*****************************************************
/* Unit tests, external id serialization
/*****************************************************
*/
private final static String EXP_SIMPLE_INT_CLASS = "{\"id\":1,\"value\":13,\"next\":1}";
private final ObjectMapper MAPPER = objectMapper();
public void testSimpleSerializationClass() throws Exception
{
Identifiable src = new Identifiable(13);
src.next = src;
// First, serialize:
String json = MAPPER.writeValueAsString(src);
assertEquals(EXP_SIMPLE_INT_CLASS, json);
// and ensure that state is cleared in-between as well:
json = MAPPER.writeValueAsString(src);
assertEquals(EXP_SIMPLE_INT_CLASS, json);
}
// Bit more complex, due to extra wrapping etc:
private final static String EXP_SIMPLE_INT_PROP = "{\"node\":{\"@id\":1,\"value\":7,\"next\":{\"node\":1}}}";
public void testSimpleSerializationProperty() throws Exception
{
IdWrapper src = new IdWrapper(7);
src.node.next = src;
// First, serialize:
String json = MAPPER.writeValueAsString(src);
assertEquals(EXP_SIMPLE_INT_PROP, json);
// and second time too, for a good measure
json = MAPPER.writeValueAsString(src);
assertEquals(EXP_SIMPLE_INT_PROP, json);
}
// [databind#370]
public void testEmptyObjectWithId() throws Exception
{
final ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(new EmptyObject());
assertEquals(aposToQuotes("{'@id':1}"), json);
}
public void testSerializeWithOpaqueStringId() throws Exception
{
StringIdentifiable ob1 = new StringIdentifiable(12);
StringIdentifiable ob2 = new StringIdentifiable(34);
ob1.next = ob2;
ob2.next = ob1;
// first just verify we get some output
String json = MAPPER.writeValueAsString(ob1);
assertNotNull(json);
// then get them back
StringIdentifiable output = MAPPER.readValue(json, StringIdentifiable.class);
assertNotNull(output);
assertEquals(12, output.value);
assertNotNull(output.next);
assertEquals(34, output.next.value);
assertSame(output.next.next, output);
String json2 = aposToQuotes("{'id':'foobar','value':3, 'next':{'id':'barf','value':5,'next':'foobar'}}");
output = MAPPER.readValue(json2, StringIdentifiable.class);
assertNotNull(output);
assertEquals(3, output.value);
assertNotNull(output.next);
assertEquals(5, output.next.value);
assertSame(output.next.next, output);
}
/*
/*****************************************************
/* Unit tests, custom (property) id serialization
/*****************************************************
*/
private final static String EXP_CUSTOM_PROP = "{\"customId\":123,\"value\":-19,\"next\":123}";
// Test for verifying that custom
public void testCustomPropertyForClass() throws Exception
{
IdentifiableWithProp src = new IdentifiableWithProp(123, -19);
src.next = src;
// First, serialize:
String json = MAPPER.writeValueAsString(src);
assertEquals(EXP_CUSTOM_PROP, json);
// and ensure that state is cleared in-between as well:
json = MAPPER.writeValueAsString(src);
assertEquals(EXP_CUSTOM_PROP, json);
}
private final static String EXP_CUSTOM_PROP_VIA_REF = "{\"node\":{\"id\":123,\"value\":7,\"next\":{\"node\":123}}}";
// Test for verifying that custom
public void testCustomPropertyViaProperty() throws Exception
{
IdWrapperCustom src = new IdWrapperCustom(123, 7);
src.node.next = src;
// First, serialize:
String json = MAPPER.writeValueAsString(src);
assertEquals(EXP_CUSTOM_PROP_VIA_REF, json);
// and second time too, for a good measure
json = MAPPER.writeValueAsString(src);
assertEquals(EXP_CUSTOM_PROP_VIA_REF, json);
}
public void testAlwaysAsId() throws Exception
{
String json = MAPPER.writeValueAsString(new AlwaysContainer());
assertEquals("{\"a\":1,\"b\":2}", json);
}
public void testAlwaysIdForTree() throws Exception
{
TreeNode root = new TreeNode(null, 1, "root");
TreeNode leaf = new TreeNode(root, 2, "leaf");
root.child = leaf;
String json = MAPPER.writeValueAsString(root);
// System.out.println(json);
assertEquals("{\"id\":1,\"name\":\"root\",\"parent\":null,\"child\":"
+"{\"id\":2,\"name\":\"leaf\",\"parent\":1,\"child\":null}}",
json);
}
//for [databind#1150]
public void testNullStringPropertyId() throws Exception
{
IdentifiableStringId value = MAPPER.readValue
(aposToQuotes("{'value':3, 'next':null, 'id':null}"), IdentifiableStringId.class);
assertNotNull(value);
assertEquals(3, value.value);
}
/*
/*****************************************************
/* Unit tests, error handling
/*****************************************************
*/
public void testInvalidProp() throws Exception
{
try {
MAPPER.writeValueAsString(new Broken());
fail("Should have thrown an exception");
} catch (JsonMappingException e) {
verifyException(e, "can not find property with name 'id'");
}
}
}