Skip to content

Commit 912a149

Browse files
committed
1 parent e2df6b1 commit 912a149

File tree

5 files changed

+253
-1
lines changed

5 files changed

+253
-1
lines changed

afterburner/src/main/java/com/fasterxml/jackson/module/afterburner/deser/SuperSonicBeanDeserializer.java

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

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<parent>
44
<groupId>com.fasterxml.jackson</groupId>
55
<artifactId>jackson-base</artifactId>
6-
<version>2.9.6</version>
6+
<version>2.9.7-SNAPSHOT</version>
77
</parent>
88
<groupId>com.fasterxml.jackson.module</groupId>
99
<artifactId>jackson-modules-base</artifactId>

release-notes/CREDITS-2.x

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,10 @@ Alexander Onnikov (aonnikov@github)
1919

2020
* Reported #44: (jaxb) `@XmlElements` does not work with `@XmlAccessorType(XmlAccessType.NONE)`
2121
(2.9.6)
22+
23+
William Headrick (headw01@github)
24+
25+
* Reported 54: Afterburner` SuperSonicBeanDeserializer` does not handle JSON Object
26+
valued Object Ids (like json)
27+
(2.9.7)
28+

release-notes/VERSION-2.x

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ Modules:
1010
=== Releases ===
1111
------------------------------------------------------------------------
1212

13+
2.9.7 (not yet released)
14+
15+
#54: Afterburner` SuperSonicBeanDeserializer` does not handle JSON Object
16+
valued Object Ids (like json)
17+
(reported by William H)
18+
1319
2.9.6 (12-Jun-2018)
1420

1521
#44: (jaxb) `@XmlElements` does not work with `@XmlAccessorType(XmlAccessType.NONE)`

0 commit comments

Comments
 (0)