/* * Copyright 2013 Nicolas Morel * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.nmorel.gwtjackson.shared.annotations; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonManagedReference; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import com.fasterxml.jackson.annotation.JsonTypeName; import com.github.nmorel.gwtjackson.shared.AbstractTester; import com.github.nmorel.gwtjackson.shared.ObjectMapperTester; /** * Test from jackson-databind and adapted for the project */ public class JsonManagedAndBackReferenceTester extends AbstractTester { /* /********************************************************** /* Test classes /********************************************************** */ /** * First, a simple 'tree': just parent/child linkage */ public 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; } } public static class SimpleTreeNode2 { public String name; private SimpleTreeNode2 parent; private 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 */ public 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 */ public static class NodeArray { @JsonManagedReference("arr") public ArrayNode[] nodes; } public 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 */ public static class NodeList { @JsonManagedReference public List<NodeForList> nodes; } public static class NodeForList { public String name; @JsonBackReference public NodeList parent; public NodeForList() { this( null ); } public NodeForList( String n ) { name = n; } } public static class NodeMap { @JsonManagedReference public Map<String, NodeForMap> nodes; } public 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 private 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 { private final String value; // So that the bean is not empty of properties private Parent parent; public Child( @JsonProperty("value") String value ) { this.value = value; } public String getValue() { return value; } @JsonBackReference public Parent getParent() { return parent; } void setParent( Parent parent ) { this.parent = parent; } } // [JACKSON-368] @JsonTypeInfo(use = Id.NAME) @JsonSubTypes({@JsonSubTypes.Type(ConcreteNode.class)}) public static abstract class AbstractNode { public String id; @JsonManagedReference public AbstractNode next; @JsonBackReference public AbstractNode prev; } @JsonTypeName("concrete") public static class ConcreteNode extends AbstractNode { public ConcreteNode() { } public ConcreteNode( String id ) { this.id = id; } } // [JACKSON-708] public static class Model708 {} public static class Advertisement708 extends Model708 { public String title; @JsonManagedReference public List<Photo708> photos; } public static class Photo708 extends Model708 { public int id; @JsonBackReference public Advertisement708 advertisement; } public static final JsonManagedAndBackReferenceTester INSTANCE = new JsonManagedAndBackReferenceTester(); private JsonManagedAndBackReferenceTester() { } /* /********************************************************** /* Unit tests /********************************************************** */ public void testBackReferenceWithoutManaged( ObjectMapperTester<SimpleTreeNode> mapper ) { SimpleTreeNode root = new SimpleTreeNode( "root" ); SimpleTreeNode child = new SimpleTreeNode( "kid" ); root.child = child; child.parent = root; String json = mapper.write( child ); assertEquals( "{\"name\":\"kid\",\"child\":null}", json ); SimpleTreeNode resultNode = mapper.read( json ); assertEquals( "kid", resultNode.name ); assertNull( resultNode.parent ); assertNull( resultNode.child ); } public void testSimpleRefs( ObjectMapperTester<SimpleTreeNode> mapper ) { SimpleTreeNode root = new SimpleTreeNode( "root" ); SimpleTreeNode child = new SimpleTreeNode( "kid" ); root.child = child; child.parent = root; String json = mapper.write( root ); assertEquals( "{\"name\":\"root\",\"child\":{\"name\":\"kid\",\"child\":null}}", json ); SimpleTreeNode resultNode = mapper.read( json ); assertEquals( "root", resultNode.name ); SimpleTreeNode resultChild = resultNode.child; assertNotNull( resultChild ); assertEquals( "kid", resultChild.name ); assertSame( resultChild.parent, resultNode ); } // [JACKSON-693] public void testSimpleRefsWithGetter( ObjectMapperTester<SimpleTreeNode2> mapper ) { SimpleTreeNode2 root = new SimpleTreeNode2( "root" ); SimpleTreeNode2 child = new SimpleTreeNode2( "kid" ); root.child = child; child.parent = root; String json = mapper.write( root ); assertEquals( "{\"name\":\"root\",\"child\":{\"name\":\"kid\",\"child\":null}}", json ); SimpleTreeNode2 resultNode = mapper.read( json ); assertEquals( "root", resultNode.name ); SimpleTreeNode2 resultChild = resultNode.child; assertNotNull( resultChild ); assertEquals( "kid", resultChild.name ); assertSame( resultChild.parent, resultNode ); } public void testFullRefs( ObjectMapperTester<FullTreeNode> mapper ) { 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.write( root ); assertEquals( "{\"name\":\"root\",\"firstChild\":{\"name\":\"kid1\",\"firstChild\":null,\"next\":{\"name\":\"kid2\"," + "" + "\"firstChild\":null,\"next\":null}},\"next\":null}", json ); FullTreeNode resultNode = mapper.read( json ); 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( ObjectMapperTester<NodeArray> mapper ) { NodeArray root = new NodeArray(); ArrayNode node1 = new ArrayNode( "a" ); ArrayNode node2 = new ArrayNode( "b" ); root.nodes = new ArrayNode[]{node1, node2}; String json = mapper.write( root ); assertEquals( "{\"nodes\":[{\"name\":\"a\"},{\"name\":\"b\"}]}", json ); NodeArray result = mapper.read( json ); 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( ObjectMapperTester<NodeList> mapper ) { NodeList root = new NodeList(); NodeForList node1 = new NodeForList( "a" ); NodeForList node2 = new NodeForList( "b" ); root.nodes = Arrays.asList( node1, node2 ); String json = mapper.write( root ); assertEquals( "{\"nodes\":[{\"name\":\"a\"},{\"name\":\"b\"}]}", json ); NodeList result = mapper.read( json ); 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( ObjectMapperTester<NodeMap> mapper ) { 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.write( root ); assertEquals( "{\"nodes\":{\"a1\":{\"name\":\"a\"},\"b2\":{\"name\":\"b\"}}}", json ); NodeMap result = mapper.read( json ); 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( ObjectMapperTester<AbstractNode> mapper ) { AbstractNode parent = new ConcreteNode( "p" ); AbstractNode child = new ConcreteNode( "c" ); parent.next = child; child.prev = parent; // serialization ought to be ok String json = mapper.write( parent ); assertEquals( "{\"@type\":\"concrete\",\"id\":\"p\",\"next\":{\"@type\":\"concrete\",\"id\":\"c\",\"next\":null}}", json ); AbstractNode root = mapper.read( json ); 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( ObjectMapperTester<Parent> mapper ) { Parent parent = new Parent(); parent.addChild( new Child( "foo" ) ); parent.addChild( new Child( "bar" ) ); String json = mapper.write( parent ); assertEquals( "{\"children\":[{\"value\":\"foo\"},{\"value\":\"bar\"}]}", json ); Parent value = mapper.read( json ); for ( Child child : value.children ) { assertEquals( value, child.getParent() ); } } public void testIssue708( ObjectMapperTester<Advertisement708> mapper ) { Advertisement708 ad = mapper.read( "{\"title\":\"Hroch\",\"photos\":[{\"id\":3}]}" ); assertNotNull( ad ); } }