• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind.struct;
2 
3 import java.util.*;
4 
5 import com.fasterxml.jackson.annotation.*;
6 import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
7 
8 import com.fasterxml.jackson.databind.*;
9 
10 public class TestParentChildReferences
11     extends BaseMapTest
12 {
13     /*
14     /**********************************************************
15     /* Test classes
16     /**********************************************************
17      */
18 
19     /**
20      * First, a simple 'tree': just parent/child linkage
21      */
22     static class SimpleTreeNode
23     {
24         public String name;
25 
26         // Reference back to parent; reference, ignored during ser,
27         // re-constructed during deser
28         @JsonBackReference
29         public SimpleTreeNode parent;
30 
31         // Reference that is serialized normally during ser, back
32         // reference within pointed-to instance assigned to point to
33         // referring bean ("this")
34         @JsonManagedReference
35         public SimpleTreeNode child;
36 
SimpleTreeNode()37         public SimpleTreeNode() { this(null); }
SimpleTreeNode(String n)38         public SimpleTreeNode(String n) { name = n; }
39     }
40 
41     static class SimpleTreeNode2
42     {
43         public String name;
44         protected SimpleTreeNode2 parent;
45         protected SimpleTreeNode2 child;
46 
SimpleTreeNode2()47         public SimpleTreeNode2() { this(null); }
SimpleTreeNode2(String n)48         public SimpleTreeNode2(String n) { name = n; }
49 
50         @JsonBackReference
getParent()51         public SimpleTreeNode2 getParent() { return parent; }
setParent(SimpleTreeNode2 p)52         public void setParent(SimpleTreeNode2 p) { parent = p; }
53 
54         @JsonManagedReference
getChild()55         public SimpleTreeNode2 getChild() { return child; }
setChild(SimpleTreeNode2 c)56         public void setChild(SimpleTreeNode2 c) { child = c; }
57     }
58 
59     /**
60      * Then nodes with two separate linkages; parent/child
61      * and prev/next-sibling
62      */
63     static class FullTreeNode
64     {
65         public String name;
66 
67         // parent-child links
68         @JsonBackReference("parent")
69         public FullTreeNode parent;
70         @JsonManagedReference("parent")
71         public FullTreeNode firstChild;
72 
73         // sibling-links
74         @JsonManagedReference("sibling")
75         public FullTreeNode next;
76         @JsonBackReference("sibling")
77         protected FullTreeNode prev;
78 
FullTreeNode()79         public FullTreeNode() { this(null); }
FullTreeNode(String name)80         public FullTreeNode(String name) {
81             this.name = name;
82         }
83     }
84 
85     /**
86      * Class for testing managed references via arrays
87      */
88     static class NodeArray
89     {
90         @JsonManagedReference("arr")
91         public ArrayNode[] nodes;
92     }
93 
94     static class ArrayNode
95     {
96         public String name;
97 
98         @JsonBackReference("arr")
99         public NodeArray parent;
100 
ArrayNode()101         public ArrayNode() { this(null); }
ArrayNode(String n)102         public ArrayNode(String n) { name = n; }
103     }
104 
105     /**
106      * Class for testing managed references via Collections
107      */
108     static class NodeList
109     {
110         @JsonManagedReference
111         public List<NodeForList> nodes;
112     }
113 
114     static class NodeForList
115     {
116         public String name;
117 
118         @JsonBackReference
119         public NodeList parent;
120 
NodeForList()121         public NodeForList() { this(null); }
NodeForList(String n)122         public NodeForList(String n) { name = n; }
123     }
124 
125     static class NodeMap
126     {
127         @JsonManagedReference
128         public Map<String,NodeForMap> nodes;
129     }
130 
131     static class NodeForMap
132     {
133         public String name;
134 
135         @JsonBackReference
136         public NodeMap parent;
137 
NodeForMap()138         public NodeForMap() { this(null); }
NodeForMap(String n)139         public NodeForMap(String n) { name = n; }
140     }
141 
142     public static class Parent {
143         @JsonManagedReference
144         protected final List<Child> children = new ArrayList<Child>();
145 
getChildren()146         public List<Child> getChildren() { return children; }
147 
addChild(Child child)148         public void addChild(Child child) { children.add(child); child.setParent(this); }
149     }
150 
151     public static class Child {
152         protected Parent parent;
153         protected final String value; // So that the bean is not empty of properties
154 
Child(@sonProperty"value") String value)155         public Child(@JsonProperty("value") String value) { this.value = value; }
156 
getValue()157         public String getValue() { return value; }
158 
159         @JsonBackReference
getParent()160         public Parent getParent() { return parent; }
161 
setParent(Parent parent)162         public void setParent(Parent parent) { this.parent = parent; }
163     }
164 
165     @JsonTypeInfo(use=Id.NAME)
166     @JsonSubTypes({@JsonSubTypes.Type(ConcreteNode.class)})
167     static abstract class AbstractNode
168     {
169         public String id;
170 
171         @JsonManagedReference public AbstractNode next;
172         @JsonBackReference public AbstractNode prev;
173     }
174 
175     @JsonTypeName("concrete")
176     static class ConcreteNode extends AbstractNode {
ConcreteNode()177         public ConcreteNode() { }
ConcreteNode(String id)178         public ConcreteNode(String id) { this.id = id; }
179     }
180 
181     // [JACKSON-708]
182     static class Model708 { }
183 
184     static class Advertisement708 extends Model708 {
185         public String title;
186         @JsonManagedReference public List<Photo708> photos;
187     }
188 
189     static class Photo708 extends Model708 {
190         public int id;
191         @JsonBackReference public Advertisement708 advertisement;
192     }
193 
194     /*
195     /**********************************************************
196     /* Unit tests
197     /**********************************************************
198      */
199 
200     private final ObjectMapper MAPPER = objectMapper();
201 
testSimpleRefs()202     public void testSimpleRefs() throws Exception
203     {
204         SimpleTreeNode root = new SimpleTreeNode("root");
205         SimpleTreeNode child = new SimpleTreeNode("kid");
206         root.child = child;
207         child.parent = root;
208 
209         String json = MAPPER.writeValueAsString(root);
210 
211         SimpleTreeNode resultNode = MAPPER.readValue(json, SimpleTreeNode.class);
212         assertEquals("root", resultNode.name);
213         SimpleTreeNode resultChild = resultNode.child;
214         assertNotNull(resultChild);
215         assertEquals("kid", resultChild.name);
216         assertSame(resultChild.parent, resultNode);
217     }
218 
219     // [JACKSON-693]
testSimpleRefsWithGetter()220     public void testSimpleRefsWithGetter() throws Exception
221     {
222         SimpleTreeNode2 root = new SimpleTreeNode2("root");
223         SimpleTreeNode2 child = new SimpleTreeNode2("kid");
224         root.child = child;
225         child.parent = root;
226 
227         String json = MAPPER.writeValueAsString(root);
228 
229         SimpleTreeNode2 resultNode = MAPPER.readValue(json, SimpleTreeNode2.class);
230         assertEquals("root", resultNode.name);
231         SimpleTreeNode2 resultChild = resultNode.child;
232         assertNotNull(resultChild);
233         assertEquals("kid", resultChild.name);
234         assertSame(resultChild.parent, resultNode);
235     }
236 
testFullRefs()237     public void testFullRefs() throws Exception
238     {
239         FullTreeNode root = new FullTreeNode("root");
240         FullTreeNode child1 = new FullTreeNode("kid1");
241         FullTreeNode child2 = new FullTreeNode("kid2");
242         root.firstChild = child1;
243         child1.parent = root;
244         child1.next = child2;
245         child2.prev = child1;
246 
247         String json = MAPPER.writeValueAsString(root);
248 
249         FullTreeNode resultNode = MAPPER.readValue(json, FullTreeNode.class);
250         assertEquals("root", resultNode.name);
251         FullTreeNode resultChild = resultNode.firstChild;
252         assertNotNull(resultChild);
253         assertEquals("kid1", resultChild.name);
254         assertSame(resultChild.parent, resultNode);
255 
256         // and then sibling linkage
257         assertNull(resultChild.prev);
258         FullTreeNode resultChild2 = resultChild.next;
259         assertNotNull(resultChild2);
260         assertEquals("kid2", resultChild2.name);
261         assertSame(resultChild, resultChild2.prev);
262         assertNull(resultChild2.next);
263     }
264 
testArrayOfRefs()265     public void testArrayOfRefs() throws Exception
266     {
267         NodeArray root = new NodeArray();
268         ArrayNode node1 = new ArrayNode("a");
269         ArrayNode node2 = new ArrayNode("b");
270         root.nodes = new ArrayNode[] { node1, node2 };
271         String json = MAPPER.writeValueAsString(root);
272 
273         NodeArray result = MAPPER.readValue(json, NodeArray.class);
274         ArrayNode[] kids = result.nodes;
275         assertNotNull(kids);
276         assertEquals(2, kids.length);
277         assertEquals("a", kids[0].name);
278         assertEquals("b", kids[1].name);
279         assertSame(result, kids[0].parent);
280         assertSame(result, kids[1].parent);
281     }
282 
testListOfRefs()283     public void testListOfRefs() throws Exception
284     {
285         NodeList root = new NodeList();
286         NodeForList node1 = new NodeForList("a");
287         NodeForList node2 = new NodeForList("b");
288         root.nodes = Arrays.asList(node1, node2);
289         String json = MAPPER.writeValueAsString(root);
290 
291         NodeList result = MAPPER.readValue(json, NodeList.class);
292         List<NodeForList> kids = result.nodes;
293         assertNotNull(kids);
294         assertEquals(2, kids.size());
295         assertEquals("a", kids.get(0).name);
296         assertEquals("b", kids.get(1).name);
297         assertSame(result, kids.get(0).parent);
298         assertSame(result, kids.get(1).parent);
299     }
300 
testMapOfRefs()301     public void testMapOfRefs() throws Exception
302     {
303         NodeMap root = new NodeMap();
304         NodeForMap node1 = new NodeForMap("a");
305         NodeForMap node2 = new NodeForMap("b");
306         Map<String,NodeForMap> nodes = new HashMap<String, NodeForMap>();
307         nodes.put("a1", node1);
308         nodes.put("b2", node2);
309         root.nodes = nodes;
310         String json = MAPPER.writeValueAsString(root);
311 
312         NodeMap result = MAPPER.readValue(json, NodeMap.class);
313         Map<String,NodeForMap> kids = result.nodes;
314         assertNotNull(kids);
315         assertEquals(2, kids.size());
316         assertNotNull(kids.get("a1"));
317         assertNotNull(kids.get("b2"));
318         assertEquals("a", kids.get("a1").name);
319         assertEquals("b", kids.get("b2").name);
320         assertSame(result, kids.get("a1").parent);
321         assertSame(result, kids.get("b2").parent);
322     }
323 
324     // for [JACKSON-368]
testAbstract368()325     public void testAbstract368() throws Exception
326     {
327         AbstractNode parent = new ConcreteNode("p");
328         AbstractNode child = new ConcreteNode("c");
329         parent.next = child;
330         child.prev = parent;
331 
332         // serialization ought to be ok
333         String json = MAPPER.writeValueAsString(parent);
334 
335         AbstractNode root = MAPPER.readValue(json, AbstractNode.class);
336 
337         assertEquals(ConcreteNode.class, root.getClass());
338         assertEquals("p", root.id);
339         assertNull(root.prev);
340         AbstractNode leaf = root.next;
341         assertNotNull(leaf);
342         assertEquals("c", leaf.id);
343         assertSame(root, leaf.prev);
344     }
345 
testIssue693()346     public void testIssue693() throws Exception
347     {
348         Parent parent = new Parent();
349         parent.addChild(new Child("foo"));
350         parent.addChild(new Child("bar"));
351         byte[] bytes = MAPPER.writeValueAsBytes(parent);
352         Parent value = MAPPER.readValue(bytes, Parent.class);
353         for (Child child : value.children) {
354             assertEquals(value, child.getParent());
355         }
356     }
357 
testIssue708()358     public void testIssue708() throws Exception
359     {
360         Advertisement708 ad = MAPPER.readValue("{\"title\":\"Hroch\",\"photos\":[{\"id\":3}]}", Advertisement708.class);
361         assertNotNull(ad);
362     }
363 }
364