• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind.objectid;
2 
3 import com.fasterxml.jackson.annotation.JsonIdentityInfo;
4 import com.fasterxml.jackson.annotation.JsonProperty;
5 import com.fasterxml.jackson.annotation.JsonTypeInfo;
6 import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
7 import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
8 import com.fasterxml.jackson.annotation.ObjectIdGenerator;
9 import com.fasterxml.jackson.core.JsonParser;
10 import com.fasterxml.jackson.databind.BaseMapTest;
11 import com.fasterxml.jackson.databind.DeserializationContext;
12 import com.fasterxml.jackson.databind.JsonDeserializer;
13 import com.fasterxml.jackson.databind.JsonMappingException;
14 import com.fasterxml.jackson.databind.JsonNode;
15 import com.fasterxml.jackson.databind.ObjectMapper;
16 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
17 
18 import java.io.IOException;
19 
20 /**
21  * Unit test(s) for [databind#622], supporting non-scalar-Object-ids,
22  * to support things like JSOG.
23  */
24 public class JSOGDeserialize622Test extends BaseMapTest
25 {
26     /** the key of the property that holds the ref */
27     public static final String REF_KEY = "@ref";
28 
29     /**
30      * JSON input
31      */
32     private static final String EXP_EXAMPLE_JSOG =  aposToQuotes(
33             "{'@id':'1','foo':66,'next':{'"+REF_KEY+"':'1'}}");
34 
35     /**
36      * Customer IdGenerator
37      */
38     static class JSOGGenerator extends ObjectIdGenerator<JSOGRef>  {
39 
40     private static final long serialVersionUID = 1L;
41     protected transient int _nextValue;
42     protected final Class<?> _scope;
43 
JSOGGenerator()44     protected JSOGGenerator() { this(null, -1); }
45 
JSOGGenerator(Class<?> scope, int nextValue)46     protected JSOGGenerator(Class<?> scope, int nextValue) {
47         _scope = scope;
48         _nextValue = nextValue;
49     }
50 
51     @Override
getScope()52     public Class<?> getScope() {
53         return _scope;
54     }
55 
56     @Override
canUseFor(ObjectIdGenerator<?> gen)57     public boolean canUseFor(ObjectIdGenerator<?> gen) {
58         return (gen.getClass() == getClass()) && (gen.getScope() == _scope);
59     }
60 
61     @Override
forScope(Class<?> scope)62     public ObjectIdGenerator<JSOGRef> forScope(Class<?> scope) {
63           return (_scope == scope) ? this : new JSOGGenerator(scope, _nextValue);
64     }
65 
66     @Override
newForSerialization(Object context)67     public ObjectIdGenerator<JSOGRef> newForSerialization(Object context) {
68           return new JSOGGenerator(_scope, 1);
69     }
70 
71     @Override
key(Object key)72     public com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey key(Object key) {
73           return new IdKey(getClass(), _scope, key);
74     }
75 
76     // important: otherwise won't get proper handling
77     @Override
maySerializeAsObject()78     public boolean maySerializeAsObject() { return true; }
79 
80     // ditto: needed for handling Object-valued Object references
81     @Override
isValidReferencePropertyName(String name, Object parser)82     public boolean isValidReferencePropertyName(String name, Object parser) {
83         return REF_KEY.equals(name);
84     }
85 
86     @Override
generateId(Object forPojo)87     public JSOGRef generateId(Object forPojo) {
88           int id = _nextValue;
89           ++_nextValue;
90           return new JSOGRef(id);
91     }
92     }
93 
94     /**
95      * The reference deserializer
96      */
97     static class JSOGRefDeserializer extends JsonDeserializer<JSOGRef>
98     {
99       @Override
deserialize(JsonParser p, DeserializationContext ctx)100       public JSOGRef deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
101           JsonNode node = p.readValueAsTree();
102           if (node.isTextual()) {
103               return new JSOGRef(node.asInt());
104           }
105           JsonNode n = node.get(REF_KEY);
106           if (n == null) {
107               throw new JsonMappingException(p, "Could not find key '"+REF_KEY
108                       +"' from ("+node.getClass().getName()+"): "+node);
109           }
110           return new JSOGRef(n.asInt());
111       }
112     }
113 
114     /**
115      * The reference object
116      */
117     @JsonDeserialize(using=JSOGRefDeserializer.class)
118     static class JSOGRef
119     {
120         @JsonProperty(REF_KEY)
121         public int ref;
122 
JSOGRef()123         public JSOGRef() { }
124 
JSOGRef(int val)125         public JSOGRef(int val) {
126             ref = val;
127         }
128 
129         @Override
toString()130         public String toString() { return "[JSOGRef#"+ref+"]"; }
131 
132         @Override
hashCode()133         public int hashCode() {
134             return ref;
135         }
136 
137         @Override
equals(Object other)138         public boolean equals(Object other) {
139             return (other instanceof JSOGRef)
140                     && ((JSOGRef) other).ref == this.ref;
141         }
142     }
143 
144     /**
145      * Example class using JSOGGenerator
146      */
147     @JsonIdentityInfo(generator=JSOGGenerator.class, property="@id")
148     public static class IdentifiableExampleJSOG {
149         public int foo;
150         public IdentifiableExampleJSOG next;
151 
IdentifiableExampleJSOG()152         protected IdentifiableExampleJSOG() { }
IdentifiableExampleJSOG(int v)153         public IdentifiableExampleJSOG(int v) {
154             foo = v;
155         }
156     }
157 
158     public static class JSOGWrapper {
159         public int value;
160 
161         @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
162         public Object jsog;
163 
JSOGWrapper()164         JSOGWrapper() { }
JSOGWrapper(int v)165         public JSOGWrapper(int v) { value = v; }
166     }
167 
168     // For [databind#669]
169 
170     @JsonIdentityInfo(generator=JSOGGenerator.class)
171     @JsonTypeInfo(use=Id.CLASS, include= As.PROPERTY, property="@class")
172     public static class Inner {
173         public String bar;
174 
Inner()175         protected Inner() {}
Inner(String bar)176         public Inner(String bar) { this.bar = bar; }
177     }
178 
179     public static class SubInner extends Inner {
180         public String extra;
181 
SubInner()182         protected SubInner() {}
SubInner(String bar, String extra)183         public SubInner(String bar, String extra) {
184             super(bar);
185             this.extra = extra;
186         }
187     }
188 
189     @JsonIdentityInfo(generator=JSOGGenerator.class)
190     public static class Outer {
191         public String foo;
192         public Inner inner1;
193         public Inner inner2;
194     }
195 
196     /*
197     /**********************************************************************
198     /* Test methods
199     /**********************************************************************
200      */
201 
202     private final ObjectMapper MAPPER = new ObjectMapper();
203 
204     // Basic for [databind#622]
testStructJSOGRef()205     public void testStructJSOGRef() throws Exception
206     {
207         IdentifiableExampleJSOG result = MAPPER.readValue(EXP_EXAMPLE_JSOG,
208                 IdentifiableExampleJSOG.class);
209         assertEquals(66, result.foo);
210         assertSame(result, result.next);
211     }
212 
213     // polymorphic alternative for [databind#622]
testPolymorphicRoundTrip()214     public void testPolymorphicRoundTrip() throws Exception
215     {
216         JSOGWrapper w = new JSOGWrapper(15);
217         // create a nice little loop
218         IdentifiableExampleJSOG ex = new IdentifiableExampleJSOG(123);
219         ex.next = ex;
220         w.jsog = ex;
221 
222         String json = MAPPER.writeValueAsString(w);
223 
224         JSOGWrapper out = MAPPER.readValue(json, JSOGWrapper.class);
225         assertNotNull(out);
226         assertEquals(15, out.value);
227         assertTrue(out.jsog instanceof IdentifiableExampleJSOG);
228         IdentifiableExampleJSOG jsog = (IdentifiableExampleJSOG) out.jsog;
229         assertEquals(123, jsog.foo);
230         assertSame(jsog, jsog.next);
231     }
232 
233     // polymorphic alternative for [databind#669]
testAlterativePolymorphicRoundTrip669()234     public void testAlterativePolymorphicRoundTrip669() throws Exception
235     {
236         Outer outer = new Outer();
237         outer.foo = "foo";
238         outer.inner1 = outer.inner2 = new SubInner("bar", "extra");
239 
240         String jsog = MAPPER.writeValueAsString(outer);
241 
242         Outer back = MAPPER.readValue(jsog, Outer.class);
243 
244         assertSame(back.inner1, back.inner2);
245     }
246 }
247