package com.fasterxml.jackson.databind.struct;
import java.util.*;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.fasterxml.jackson.databind.*;
public class TestParentChildReferences
extends BaseMapTest
{
/*
/**********************************************************
/* Test classes
/**********************************************************
*/
/**
* First, a simple 'tree': just parent/child linkage
*/
static class SimpleTreeNode
{
public String name;
// Reference back to parent; reference, ignored during ser,
// re-constructed during deser
@JsonBackReference
public SimpleTreeNode parent;
// Reference that is serialized normally during ser, back
// reference within pointed-to instance assigned to point to
// referring bean ("this")
@JsonManagedReference
public SimpleTreeNode child;
public SimpleTreeNode() { this(null); }
public SimpleTreeNode(String n) { name = n; }
}
static class SimpleTreeNode2
{
public String name;
protected SimpleTreeNode2 parent;
protected SimpleTreeNode2 child;
public SimpleTreeNode2() { this(null); }
public SimpleTreeNode2(String n) { name = n; }
@JsonBackReference
public SimpleTreeNode2 getParent() { return parent; }
public void setParent(SimpleTreeNode2 p) { parent = p; }
@JsonManagedReference
public SimpleTreeNode2 getChild() { return child; }
public void setChild(SimpleTreeNode2 c) { child = c; }
}
/**
* Then nodes with two separate linkages; parent/child
* and prev/next-sibling
*/
static class FullTreeNode
{
public String name;
// parent-child links
@JsonBackReference("parent")
public FullTreeNode parent;
@JsonManagedReference("parent")
public FullTreeNode firstChild;
// sibling-links
@JsonManagedReference("sibling")
public FullTreeNode next;
@JsonBackReference("sibling")
protected FullTreeNode prev;
public FullTreeNode() { this(null); }
public FullTreeNode(String name) {
this.name = name;
}
}
/**
* Class for testing managed references via arrays
*/
static class NodeArray
{
@JsonManagedReference("arr")
public ArrayNode[] nodes;
}
static class ArrayNode
{
public String name;
@JsonBackReference("arr")
public NodeArray parent;
public ArrayNode() { this(null); }
public ArrayNode(String n) { name = n; }
}
/**
* Class for testing managed references via Collections
*/
static class NodeList
{
@JsonManagedReference
public List<NodeForList> nodes;
}
static class NodeForList
{
public String name;
@JsonBackReference
public NodeList parent;
public NodeForList() { this(null); }
public NodeForList(String n) { name = n; }
}
static class NodeMap
{
@JsonManagedReference
public Map<String,NodeForMap> nodes;
}
static class NodeForMap
{
public String name;
@JsonBackReference
public NodeMap parent;
public NodeForMap() { this(null); }
public NodeForMap(String n) { name = n; }
}
public static class Parent {
@JsonManagedReference
protected final List<Child> children = new ArrayList<Child>();
public List<Child> getChildren() { return children; }
public void addChild(Child child) { children.add(child); child.setParent(this); }
}
public static class Child {
protected Parent parent;
protected final String value; // So that the bean is not empty of properties
public Child(@JsonProperty("value") String value) { this.value = value; }
public String getValue() { return value; }
@JsonBackReference
public Parent getParent() { return parent; }
public void setParent(Parent parent) { this.parent = parent; }
}
@JsonTypeInfo(use=Id.NAME)
@JsonSubTypes({@JsonSubTypes.Type(ConcreteNode.class)})
static abstract class AbstractNode
{
public String id;
@JsonManagedReference public AbstractNode next;
@JsonBackReference public AbstractNode prev;
}
@JsonTypeName("concrete")
static class ConcreteNode extends AbstractNode {
public ConcreteNode() { }
public ConcreteNode(String id) { this.id = id; }
}
// [JACKSON-708]
static class Model708 { }
static class Advertisement708 extends Model708 {
public String title;
@JsonManagedReference public List<Photo708> photos;
}
static class Photo708 extends Model708 {
public int id;
@JsonBackReference public Advertisement708 advertisement;
}
/*
/**********************************************************
/* Unit tests
/**********************************************************
*/
private final ObjectMapper MAPPER = objectMapper();
public void testSimpleRefs() throws Exception
{
SimpleTreeNode root = new SimpleTreeNode("root");
SimpleTreeNode child = new SimpleTreeNode("kid");
root.child = child;
child.parent = root;
String json = MAPPER.writeValueAsString(root);
SimpleTreeNode resultNode = MAPPER.readValue(json, SimpleTreeNode.class);
assertEquals("root", resultNode.name);
SimpleTreeNode resultChild = resultNode.child;
assertNotNull(resultChild);
assertEquals("kid", resultChild.name);
assertSame(resultChild.parent, resultNode);
}
// [JACKSON-693]
public void testSimpleRefsWithGetter() throws Exception
{
SimpleTreeNode2 root = new SimpleTreeNode2("root");
SimpleTreeNode2 child = new SimpleTreeNode2("kid");
root.child = child;
child.parent = root;
String json = MAPPER.writeValueAsString(root);
SimpleTreeNode2 resultNode = MAPPER.readValue(json, SimpleTreeNode2.class);
assertEquals("root", resultNode.name);
SimpleTreeNode2 resultChild = resultNode.child;
assertNotNull(resultChild);
assertEquals("kid", resultChild.name);
assertSame(resultChild.parent, resultNode);
}
public void testFullRefs() throws Exception
{
FullTreeNode root = new FullTreeNode("root");
FullTreeNode child1 = new FullTreeNode("kid1");
FullTreeNode child2 = new FullTreeNode("kid2");
root.firstChild = child1;
child1.parent = root;
child1.next = child2;
child2.prev = child1;
String json = MAPPER.writeValueAsString(root);
FullTreeNode resultNode = MAPPER.readValue(json, FullTreeNode.class);
assertEquals("root", resultNode.name);
FullTreeNode resultChild = resultNode.firstChild;
assertNotNull(resultChild);
assertEquals("kid1", resultChild.name);
assertSame(resultChild.parent, resultNode);
// and then sibling linkage
assertNull(resultChild.prev);
FullTreeNode resultChild2 = resultChild.next;
assertNotNull(resultChild2);
assertEquals("kid2", resultChild2.name);
assertSame(resultChild, resultChild2.prev);
assertNull(resultChild2.next);
}
public void testArrayOfRefs() throws Exception
{
NodeArray root = new NodeArray();
ArrayNode node1 = new ArrayNode("a");
ArrayNode node2 = new ArrayNode("b");
root.nodes = new ArrayNode[] { node1, node2 };
String json = MAPPER.writeValueAsString(root);
NodeArray result = MAPPER.readValue(json, NodeArray.class);
ArrayNode[] kids = result.nodes;
assertNotNull(kids);
assertEquals(2, kids.length);
assertEquals("a", kids[0].name);
assertEquals("b", kids[1].name);
assertSame(result, kids[0].parent);
assertSame(result, kids[1].parent);
}
public void testListOfRefs() throws Exception
{
NodeList root = new NodeList();
NodeForList node1 = new NodeForList("a");
NodeForList node2 = new NodeForList("b");
root.nodes = Arrays.asList(node1, node2);
String json = MAPPER.writeValueAsString(root);
NodeList result = MAPPER.readValue(json, NodeList.class);
List<NodeForList> kids = result.nodes;
assertNotNull(kids);
assertEquals(2, kids.size());
assertEquals("a", kids.get(0).name);
assertEquals("b", kids.get(1).name);
assertSame(result, kids.get(0).parent);
assertSame(result, kids.get(1).parent);
}
public void testMapOfRefs() throws Exception
{
NodeMap root = new NodeMap();
NodeForMap node1 = new NodeForMap("a");
NodeForMap node2 = new NodeForMap("b");
Map<String,NodeForMap> nodes = new HashMap<String, NodeForMap>();
nodes.put("a1", node1);
nodes.put("b2", node2);
root.nodes = nodes;
String json = MAPPER.writeValueAsString(root);
NodeMap result = MAPPER.readValue(json, NodeMap.class);
Map<String,NodeForMap> kids = result.nodes;
assertNotNull(kids);
assertEquals(2, kids.size());
assertNotNull(kids.get("a1"));
assertNotNull(kids.get("b2"));
assertEquals("a", kids.get("a1").name);
assertEquals("b", kids.get("b2").name);
assertSame(result, kids.get("a1").parent);
assertSame(result, kids.get("b2").parent);
}
// for [JACKSON-368]
public void testAbstract368() throws Exception
{
AbstractNode parent = new ConcreteNode("p");
AbstractNode child = new ConcreteNode("c");
parent.next = child;
child.prev = parent;
// serialization ought to be ok
String json = MAPPER.writeValueAsString(parent);
AbstractNode root = MAPPER.readValue(json, AbstractNode.class);
assertEquals(ConcreteNode.class, root.getClass());
assertEquals("p", root.id);
assertNull(root.prev);
AbstractNode leaf = root.next;
assertNotNull(leaf);
assertEquals("c", leaf.id);
assertSame(root, leaf.prev);
}
public void testIssue693() throws Exception
{
Parent parent = new Parent();
parent.addChild(new Child("foo"));
parent.addChild(new Child("bar"));
byte[] bytes = MAPPER.writeValueAsBytes(parent);
Parent value = MAPPER.readValue(bytes, Parent.class);
for (Child child : value.children) {
assertEquals(value, child.getParent());
}
}
public void testIssue708() throws Exception
{
Advertisement708 ad = MAPPER.readValue("{\"title\":\"Hroch\",\"photos\":[{\"id\":3}]}", Advertisement708.class);
assertNotNull(ad);
}
}