ObjectArrayForwardReference6205Test.java
package tools.jackson.databind.objectid;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.*;
import tools.jackson.databind.*;
import tools.jackson.databind.testutil.DatabindTestUtil;
import static org.junit.jupiter.api.Assertions.*;
// [databind#6205]
/**
* Tests that forward references within Object arrays are resolved into the slot they
* were read from, whatever order they get resolved in, and whatever else the array holds.
*/
public class ObjectArrayForwardReference6205Test extends DatabindTestUtil
{
static final class Container {
public Node first;
@JsonIdentityReference(alwaysAsId = true)
public Node[] refs;
public Node[] defs;
}
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
static class Node {
public int id;
public String name;
}
private final ObjectMapper MAPPER = newJsonMapper();
// Definitions in the same order as the references
@Test
public void forwardOrderResolution() throws Exception {
_verify(_document(1000, false), 1000, false);
}
// ... and in the reverse order, so that resolution runs back to front
@Test
public void reverseOrderResolution() throws Exception {
_verify(_document(1000, true), 1000, true);
}
// Already-resolved entries sit in the same array as pending ones: placement of
// the pending ones must not be disturbed by them
@Test
public void referencesMixedWithInlineValues() throws Exception {
// "0" is defined before "refs" so references to it resolve inline, but
// 1 and 2 are only defined after it: those references, at slots 1 and 3,
// are the only pending ones
String json = a2q("{'first':{'id':0,'name':'zero'},"
+"'refs':[0,1,0,2],"
+"'defs':[{'id':1,'name':'one'},{'id':2,'name':'two'}]}");
Container c = MAPPER.readValue(json, Container.class);
assertEquals(4, c.refs.length);
Node[] expected = new Node[] { c.first, c.defs[0], c.first, c.defs[1] };
for (int i = 0; i < expected.length; ++i) {
assertNotNull(c.refs[i], "Null entry at #"+i);
assertSame(expected[i], c.refs[i], "Not same instance at #"+i);
}
}
private String _document(int count, boolean reverse) {
StringBuilder sb = new StringBuilder("{'refs':[");
for (int i = 0; i < count; ++i) {
sb.append(i).append((i == count-1) ? "" : ",");
}
sb.append("],'defs':[");
for (int i = 0; i < count; ++i) {
int id = reverse ? (count - 1 - i) : i;
sb.append("{'id':").append(id).append(",'name':'n").append(id).append("'}");
sb.append((i == count-1) ? "" : ",");
}
return a2q(sb.append("]}").toString());
}
private void _verify(String json, int count, boolean reverse) throws Exception {
Container c = MAPPER.readValue(json, Container.class);
assertEquals(count, c.refs.length);
for (int i = 0; i < count; ++i) {
assertNotNull(c.refs[i], "Null entry at #"+i);
assertEquals(i, c.refs[i].id, "Wrong entry at #"+i);
int defIndex = reverse ? (count - 1 - i) : i;
assertSame(c.defs[defIndex], c.refs[i], "Not same instance at #"+i);
}
}
}