Skip to content

Commit 59af571

Browse files
committed
Fix #742 for 2.5.3
1 parent 50ea083 commit 59af571

File tree

5 files changed

+71
-21
lines changed

5 files changed

+71
-21
lines changed

src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ public DefaultDeserializationContext copy() {
8181
@Override
8282
public ReadableObjectId findObjectId(Object id, ObjectIdGenerator<?> gen, ObjectIdResolver resolverType)
8383
{
84+
/* 02-Apr-2015, tatu: As per [databind#742] should allow 'null', similar to how
85+
* missing id already works.
86+
*/
87+
if (id == null) {
88+
return null;
89+
}
90+
8491
final ObjectIdGenerator.IdKey key = gen.key(id);
8592

8693
if (_objectIds == null) {

src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdValueProperty.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,16 @@ public Object deserializeSetAndReturn(JsonParser jp,
8383
{
8484
// note: no null checks (unlike usually); deserializer should fail if one found
8585
Object id = _valueDeserializer.deserialize(jp, ctxt);
86+
87+
/* 02-Apr-2015, tatu: Actually, as per [databind#742], let it be;
88+
* missing or null id is needed for some cases, such as cases where id
89+
* will be generated externally, at a later point, and is not available
90+
* quite yet. Typical use case is with DB inserts.
91+
*/
92+
if (id == null) {
93+
return null;
94+
}
95+
8696
ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator, _objectIdReader.resolver);
8797
roid.bindItem(instance);
8898
// also: may need to set a property value as well

src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedObjectIdGenerator.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ public ObjectIdGenerator<Object> newForSerialization(Object context) {
3030

3131
@Override
3232
public com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey key(Object key) {
33+
if (key == null) {
34+
return null;
35+
}
3336
// should we use general type for all; or type of property itself?
3437
return new IdKey(getClass(), _scope, key);
3538
}

src/main/java/com/fasterxml/jackson/databind/ser/impl/PropertyBasedObjectIdGenerator.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ public ObjectIdGenerator<Object> newForSerialization(Object context) {
7070

7171
@Override
7272
public com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey key(Object key) {
73+
if (key == null) {
74+
return null;
75+
}
7376
// should we use general type for all; or type of property itself?
7477
return new IdKey(getClass(), _scope, key);
7578
}

src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdDeserialization.java

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -188,22 +188,22 @@ public ObjectIdResolver newForDeserialization(Object c)
188188
/*****************************************************
189189
*/
190190

191-
private final ObjectMapper mapper = new ObjectMapper();
191+
private final ObjectMapper MAPPER = new ObjectMapper();
192192

193193
private final static String EXP_SIMPLE_INT_CLASS = "{\"id\":1,\"value\":13,\"next\":1}";
194194

195195
public void testSimpleDeserializationClass() throws Exception
196196
{
197197
// then bring back...
198-
Identifiable result = mapper.readValue(EXP_SIMPLE_INT_CLASS, Identifiable.class);
198+
Identifiable result = MAPPER.readValue(EXP_SIMPLE_INT_CLASS, Identifiable.class);
199199
assertEquals(13, result.value);
200200
assertSame(result, result.next);
201201
}
202202

203203
// Should be ok NOT to have Object id, as well
204204
public void testMissingObjectId() throws Exception
205205
{
206-
Identifiable result = mapper.readValue(aposToQuotes("{'value':28, 'next':{'value':29}}"),
206+
Identifiable result = MAPPER.readValue(aposToQuotes("{'value':28, 'next':{'value':29}}"),
207207
Identifiable.class);
208208
assertNotNull(result);
209209
assertEquals(28, result.value);
@@ -222,10 +222,10 @@ public void testSimpleUUIDForClassRoundTrip() throws Exception
222222
child2.parent = root;
223223
child1.first = child2;
224224

225-
String json = mapper.writeValueAsString(root);
225+
String json = MAPPER.writeValueAsString(root);
226226

227227
// and should come back the same too...
228-
UUIDNode result = mapper.readValue(json, UUIDNode.class);
228+
UUIDNode result = MAPPER.readValue(json, UUIDNode.class);
229229
assertEquals(1, result.value);
230230
UUIDNode result2 = result.first;
231231
UUIDNode result3 = result.second;
@@ -244,15 +244,15 @@ public void testSimpleUUIDForClassRoundTrip() throws Exception
244244

245245
public void testSimpleDeserializationProperty() throws Exception
246246
{
247-
IdWrapper result = mapper.readValue(EXP_SIMPLE_INT_PROP, IdWrapper.class);
247+
IdWrapper result = MAPPER.readValue(EXP_SIMPLE_INT_PROP, IdWrapper.class);
248248
assertEquals(7, result.node.value);
249249
assertSame(result.node, result.node.next.node);
250250
}
251251

252252
// Another test to ensure ordering is not required (i.e. can do front references)
253253
public void testSimpleDeserWithForwardRefs() throws Exception
254254
{
255-
IdWrapper result = mapper.readValue("{\"node\":{\"value\":7,\"next\":{\"node\":1}, \"@id\":1}}"
255+
IdWrapper result = MAPPER.readValue("{\"node\":{\"value\":7,\"next\":{\"node\":1}, \"@id\":1}}"
256256
,IdWrapper.class);
257257
assertEquals(7, result.node.value);
258258
assertSame(result.node, result.node.next.node);
@@ -265,7 +265,7 @@ public void testForwardReference()
265265
+ "{\"id\":1,\"name\":\"First\",\"manager\":2,\"reports\":[]},"
266266
+ "{\"id\":2,\"name\":\"Second\",\"manager\":null,\"reports\":[1]}"
267267
+ "]}";
268-
Company company = mapper.readValue(json, Company.class);
268+
Company company = MAPPER.readValue(json, Company.class);
269269
assertEquals(2, company.employees.size());
270270
Employee firstEmployee = company.employees.get(0);
271271
Employee secondEmployee = company.employees.get(1);
@@ -282,7 +282,7 @@ public void testForwardReferenceInCollection()
282282
+ "{\"id\":1,\"name\":\"First\",\"manager\":null,\"reports\":[2]},"
283283
+ "{\"id\":2,\"name\":\"Second\",\"manager\":1,\"reports\":[]}"
284284
+ "]}";
285-
Company company = mapper.readValue(json, Company.class);
285+
Company company = MAPPER.readValue(json, Company.class);
286286
assertEquals(2, company.employees.size());
287287
Employee firstEmployee = company.employees.get(0);
288288
Employee secondEmployee = company.employees.get(1);
@@ -297,7 +297,7 @@ public void testForwardReferenceInMap()
297297
+ "\"2\": 2,"
298298
+ "\"3\":{\"id\":2,\"name\":\"Second\",\"manager\":1,\"reports\":[]}"
299299
+ "}}";
300-
MappedCompany company = mapper.readValue(json, MappedCompany.class);
300+
MappedCompany company = MAPPER.readValue(json, MappedCompany.class);
301301
assertEquals(3, company.employees.size());
302302
Employee firstEmployee = company.employees.get(1);
303303
Employee secondEmployee = company.employees.get(3);
@@ -315,7 +315,7 @@ private void assertEmployees(Employee firstEmployee, Employee secondEmployee)
315315

316316
public void testForwardReferenceAnySetterCombo() throws Exception {
317317
String json = "{\"@id\":1, \"foo\":2, \"bar\":{\"@id\":2, \"foo\":1}}";
318-
AnySetterObjectId value = mapper.readValue(json, AnySetterObjectId.class);
318+
AnySetterObjectId value = MAPPER.readValue(json, AnySetterObjectId.class);
319319
assertSame(value.values.get("bar"), value.values.get("foo"));
320320
}
321321

@@ -327,7 +327,7 @@ public void testUnresolvedForwardReference()
327327
+ "{\"id\":2,\"name\":\"Second\",\"manager\":3,\"reports\":[]}"
328328
+ "]}";
329329
try {
330-
mapper.readValue(json, Company.class);
330+
MAPPER.readValue(json, Company.class);
331331
fail("Should have thrown.");
332332
} catch (UnresolvedForwardReference exception) {
333333
// Expected
@@ -345,7 +345,7 @@ public void testUnresolvedForwardReference()
345345
// [databind#299]: Allow unresolved ids to become nulls
346346
public void testUnresolvableAsNull() throws Exception
347347
{
348-
IdWrapper w = mapper.reader(IdWrapper.class)
348+
IdWrapper w = MAPPER.reader(IdWrapper.class)
349349
.without(DeserializationFeature.FAIL_ON_UNRESOLVED_OBJECT_IDS)
350350
.readValue(aposToQuotes("{'node':123}"));
351351
assertNotNull(w);
@@ -358,7 +358,7 @@ public void testKeepCollectionOrdering() throws Exception
358358
+ "{\"id\":1,\"name\":\"First\",\"manager\":null,\"reports\":[2]},"
359359
+ "{\"id\":2,\"name\":\"Second\",\"manager\":1,\"reports\":[]}"
360360
+ "]}";
361-
Company company = mapper.readValue(json, Company.class);
361+
Company company = MAPPER.readValue(json, Company.class);
362362
assertEquals(4, company.employees.size());
363363
// Deser must keep object ordering.
364364
Employee firstEmployee = company.employees.get(1);
@@ -376,7 +376,7 @@ public void testKeepMapOrdering()
376376
+ "\"3\":{\"id\":1,\"name\":\"First\",\"manager\":null,\"reports\":[2]},"
377377
+ "\"4\":{\"id\":2,\"name\":\"Second\",\"manager\":1,\"reports\":[]}"
378378
+ "}}";
379-
MappedCompany company = mapper.readValue(json, MappedCompany.class);
379+
MappedCompany company = MAPPER.readValue(json, MappedCompany.class);
380380
assertEquals(4, company.employees.size());
381381
Employee firstEmployee = company.employees.get(2);
382382
Employee secondEmployee = company.employees.get(1);
@@ -401,7 +401,7 @@ public void testKeepMapOrdering()
401401
public void testCustomDeserializationClass() throws Exception
402402
{
403403
// then bring back...
404-
IdentifiableCustom result = mapper.readValue(EXP_CUSTOM_VIA_CLASS, IdentifiableCustom.class);
404+
IdentifiableCustom result = MAPPER.readValue(EXP_CUSTOM_VIA_CLASS, IdentifiableCustom.class);
405405
assertEquals(-900, result.value);
406406
assertSame(result, result.next);
407407
}
@@ -411,7 +411,7 @@ public void testCustomDeserializationClass() throws Exception
411411
public void testCustomDeserializationProperty() throws Exception
412412
{
413413
// then bring back...
414-
IdWrapperExt result = mapper.readValue(EXP_CUSTOM_VIA_PROP, IdWrapperExt.class);
414+
IdWrapperExt result = MAPPER.readValue(EXP_CUSTOM_VIA_PROP, IdWrapperExt.class);
415415
assertEquals(99, result.node.value);
416416
assertSame(result.node, result.node.next.node);
417417
assertEquals(3, result.node.customId);
@@ -422,21 +422,48 @@ public void testCustomDeserializationProperty() throws Exception
422422
/* Unit tests, custom id resolver
423423
/*****************************************************
424424
*/
425-
public void testCustomPoolResolver()
426-
throws Exception
425+
426+
public void testCustomPoolResolver() throws Exception
427427
{
428428
Map<Object,WithCustomResolution> pool = new HashMap<Object,WithCustomResolution>();
429429
pool.put(1, new WithCustomResolution(1, 1));
430430
pool.put(2, new WithCustomResolution(2, 2));
431431
pool.put(3, new WithCustomResolution(3, 3));
432432
pool.put(4, new WithCustomResolution(4, 4));
433433
pool.put(5, new WithCustomResolution(5, 5));
434-
ContextAttributes attrs = mapper.getDeserializationConfig().getAttributes().withSharedAttribute(POOL_KEY, pool);
434+
ContextAttributes attrs = MAPPER.getDeserializationConfig().getAttributes().withSharedAttribute(POOL_KEY, pool);
435435
String content = "{\"data\":[1,2,3,4,5]}";
436-
CustomResolutionWrapper wrapper = mapper.reader(CustomResolutionWrapper.class).with(attrs).readValue(content);
436+
CustomResolutionWrapper wrapper = MAPPER.reader(CustomResolutionWrapper.class).with(attrs).readValue(content);
437437
assertFalse(wrapper.data.isEmpty());
438438
for (WithCustomResolution ob : wrapper.data) {
439439
assertSame(pool.get(ob.id), ob);
440440
}
441441
}
442+
443+
/*
444+
/*****************************************************
445+
/* Unit tests, missing/null Object id [databind#742]
446+
/*****************************************************
447+
*/
448+
449+
/*
450+
private final static String EXP_SIMPLE_INT_CLASS = "{\"id\":1,\"value\":13,\"next\":1}";
451+
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="id")
452+
static class Identifiable
453+
{
454+
public int value;
455+
456+
public Identifiable next;
457+
}
458+
*/
459+
460+
public void testNullObjectId() throws Exception
461+
{
462+
// Ok, so missing Object Id is ok, but so is null.
463+
464+
Identifiable value = MAPPER.readValue
465+
(aposToQuotes("{'value':3, 'next':null, 'id':null}"), Identifiable.class);
466+
assertNotNull(value);
467+
assertEquals(3, value.value);
468+
}
442469
}

0 commit comments

Comments
 (0)