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