Skip to content

Commit

Permalink
Fix #1261
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Jun 15, 2016
1 parent 522d8f6 commit 567d6c0
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 218 deletions.
5 changes: 5 additions & 0 deletions release-notes/CREDITS
Original file line number Diff line number Diff line change
Expand Up @@ -494,3 +494,8 @@ Maarten Billemont (lhunath@github)
Vladimir Kulev (lightoze@github)
* Reported #1028: Ignore USE_BIG_DECIMAL_FOR_FLOATS for NaN/Infinity
(2.8.0)

Ari Fogel (arifogel@github)
* Reported 1261, contributed fix for: `@JsonIdentityInfo` deserialization fails with
combination of forward references, `@JsonCreator`
(2.8.0)
2 changes: 2 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ Project: jackson-databind
#1233: Add support for `JsonFormat.Feature.WRITE_SORTED_MAP_ENTRIES`
#1235: `java.nio.file.Path` support incomplete
(reported by, fix contributed by Benson M)
#1261: JsonIdentityInfo broken deserialization involving forward references and/or cycles
(reported by, fix contributed by Ari F)

2.7.5 (11-Jun-2016)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.impl.*;
import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.Referring;
import com.fasterxml.jackson.databind.util.NameTransformer;
import com.fasterxml.jackson.databind.util.TokenBuffer;

Expand Down Expand Up @@ -384,6 +385,7 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, final Deseri
TokenBuffer unknown = null;

JsonToken t = p.getCurrentToken();
List<BeanReferring> referrings = null;
for (; t == JsonToken.FIELD_NAME; t = p.nextToken()) {
String propName = p.getCurrentName();
p.nextToken(); // to point to value
Expand Down Expand Up @@ -426,11 +428,21 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, final Deseri
// regular property? needs buffering
SettableBeanProperty prop = _beanProperties.find(propName);
if (prop != null) {
buffer.bufferProperty(prop, _deserializeWithErrorWrapping(p, ctxt, prop));
try {
buffer.bufferProperty(prop, _deserializeWithErrorWrapping(p, ctxt, prop));
} catch (UnresolvedForwardReference reference) {
// 14-Jun-2016, tatu: As per [databind#1261], looks like we need additional
// handling of forward references here. Not exactly sure why existing
// facilities did not cover, but this does appear to solve the problem
BeanReferring referring = handleUnresolvedReference(p, prop, buffer, reference);
if (referrings == null) {
referrings = new ArrayList<BeanReferring>();
}
referrings.add(referring);
}
continue;
}
// As per [JACKSON-313], things marked as ignorable should not be
// passed to any setter
// Things marked as ignorable should not be passed to any setter
if (_ignorableProps != null && _ignorableProps.contains(propName)) {
handleIgnoredProperty(p, ctxt, handledType(), propName);
continue;
Expand Down Expand Up @@ -460,6 +472,11 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, final Deseri
wrapInstantiationProblem(e, ctxt);
bean = null; // never gets here
}
if (referrings != null) {
for (BeanReferring referring : referrings) {
referring.setBean(bean);
}
}
if (unknown != null) {
// polymorphic?
if (bean.getClass() != _beanType.getRawClass()) {
Expand All @@ -471,6 +488,20 @@ protected Object _deserializeUsingPropertyBased(final JsonParser p, final Deseri
return bean;
}

/**
* @since 2.8
*/
private BeanReferring handleUnresolvedReference(JsonParser p,
SettableBeanProperty prop, PropertyValueBuffer buffer,
UnresolvedForwardReference reference)
throws JsonMappingException
{
BeanReferring referring = new BeanReferring(reference, prop.getType().getRawClass(),
buffer, prop);
reference.getRoid().appendReferring(referring);
return referring;
}

protected final Object _deserializeWithErrorWrapping(JsonParser p,
DeserializationContext ctxt, SettableBeanProperty prop)
throws IOException
Expand Down Expand Up @@ -920,4 +951,28 @@ protected Exception _creatorReturnedNullException() {
}
return _nullFromCreator;
}

/**
* @since 2.8
*/
static class BeanReferring extends Referring {
private final SettableBeanProperty _prop;
private Object _bean;

public void setBean(Object bean) {
_bean = bean;
}

BeanReferring(UnresolvedForwardReference ref,
Class<?> valueType, PropertyValueBuffer buffer, SettableBeanProperty prop)
{
super(ref, valueType);
_prop = prop;
}

@Override
public void handleResolvedForwardReference(Object id, Object value) throws IOException {
_prop.set(_bean, value);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,6 @@ public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
vd = findConvertingContentDeserializer(ctxt, property, vd);
}
final JavaType vt = _mapType.getContentType();
System.err.println("Map deser for "+_mapType+":\n vt == "+vt);
if (vd == null) {
vd = ctxt.findContextualValueDeserializer(vt, property);
} else { // if directly assigned, probably not yet contextual, so:
Expand Down Expand Up @@ -668,7 +667,7 @@ public void resolveForwardReference(Object id, Object value) throws IOException
* The resolved object associated with {@link #key} comes before the values in
* {@link #next}.
*/
final static class MapReferring extends Referring {
static class MapReferring extends Referring {
private final MapReferringAccumulator _parent;

public final Map<Object, Object> next = new LinkedHashMap<Object, Object>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.fasterxml.jackson.databind.objectid;

import java.util.*;

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.*;

public class ObjectWithCreator1261Test
extends BaseMapTest
{
static class Answer
{
public SortedMap<String, Parent> parents;

public Answer() {
parents = new TreeMap<String, Parent>();
}
}

@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@id")
static class Parent
{
public Map<String, Child> children;

@JsonIdentityReference(alwaysAsId = true)
public Child favoriteChild;

public String name;

protected Parent() { }

public Parent(String name, boolean ignored) {
children = new TreeMap<String, Child>();
this.name = name;
}
}

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@id")
static class Child
{
public String name;

@JsonIdentityReference(alwaysAsId = true)
public Parent parent;

@JsonIdentityReference(alwaysAsId = true)
public List<Parent> parentAsList;

public String someNullProperty;

protected Child() { }

@JsonCreator
public Child(@JsonProperty("name") String name,
@JsonProperty("someNullProperty") String someNullProperty) {
this.name = name;
this.someNullProperty = someNullProperty;
}

public Child(String n) {
name = n;
}
}

public void testObjectIds1261() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);

Answer initialAnswer = createInitialAnswer();
String initialAnswerString = mapper.writeValueAsString(initialAnswer);
// System.out.println("Initial answer:\n"+initialAnswerString);
JsonNode tree = mapper.readTree(initialAnswerString);
Answer deserializedAnswer = mapper.readValue(initialAnswerString,
Answer.class);
String reserializedAnswerString = mapper
.writeValueAsString(deserializedAnswer);
JsonNode newTree = mapper.readTree(reserializedAnswerString);
if (!tree.equals(newTree)) {
fail("Original and recovered Json are different. Recovered = \n"
+ reserializedAnswerString + "\n");
}
}

private Answer createInitialAnswer() {
Answer answer = new Answer();
String child1Name = "child1";
String child2Name = "child2";
String parent1Name = "parent1";
String parent2Name = "parent2";
Parent parent1 = new Parent(parent1Name, false);
answer.parents.put(parent1Name, parent1);
Child child1 = new Child(child1Name);
child1.parent = parent1;
child1.parentAsList = Collections.singletonList(parent1);
Child child2 = new Child(child2Name);
Parent parent2 = new Parent(parent2Name, false);
child2.parent = parent2;
child2.parentAsList = Collections.singletonList(parent2);
parent1.children.put(child1Name, child1);
parent1.children.put(child2Name, child2);
answer.parents.put(parent2Name, parent2);
return answer;
}
}
Loading

0 comments on commit 567d6c0

Please sign in to comment.