package com.fasterxml.jackson.databind.objectid; import java.util.*; import com.fasterxml.jackson.annotation.JsonIdentityInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.ObjectIdGenerators; import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping; public class TestObjectIdWithPolymorphic extends BaseMapTest { @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="id") static abstract class Base { public int value; public Base next; public Base() { this(0); } public Base(int v) { value = v; } } static class Impl extends Base { public int extra; public Impl() { this(0, 0); } public Impl(int v, int e) { super(v); extra = e; } } // [JACKSON-811] types @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id") public static class Base811 { public int id; public Base811 owner; protected Base811() {} public Base811(Process owner) { this.owner = owner; if (owner == null) { id = 0; } else { id = ++owner.childIdCounter; owner.children.add(this); } } } public static class Process extends Base811 { protected int childIdCounter = 0; protected List<Base811> children = new ArrayList<Base811>(); public Process() { super(null); } } public static abstract class Activity extends Base811 { protected Activity parent; public Activity(Process owner, Activity parent) { super(owner); this.parent = parent; } protected Activity() { super(); } } public static class Scope extends Activity { public final List<FaultHandler> faultHandlers = new ArrayList<FaultHandler>(); public Scope(Process owner, Activity parent) { super(owner, parent); } protected Scope() { super(); } } public static class FaultHandler extends Base811 { public final List<Catch> catchBlocks = new ArrayList<Catch>(); public FaultHandler(Process owner) { super(owner); } protected FaultHandler() {} } public static class Catch extends Scope { public Catch(Process owner, Activity parent) { super(owner, parent); } protected Catch() {}; } /* /***************************************************** /* Unit tests for polymorphic type handling /***************************************************** */ private final ObjectMapper mapper = new ObjectMapper(); public void testPolymorphicRoundtrip() throws Exception { // create simple 2 node loop: Impl in1 = new Impl(123, 456); in1.next = new Impl(111, 222); in1.next.next = in1; String json = mapper.writeValueAsString(in1); // then bring back... Base result0 = mapper.readValue(json, Base.class); assertNotNull(result0); assertSame(Impl.class, result0.getClass()); Impl result = (Impl) result0; assertEquals(123, result.value); assertEquals(456, result.extra); Impl result2 = (Impl) result.next; assertEquals(111, result2.value); assertEquals(222, result2.extra); assertSame(result, result2.next); } public void testIssue811() throws Exception { ObjectMapper om = new ObjectMapper(); om.enable(SerializationFeature.WRITE_ENUMS_USING_INDEX); om.enable(SerializationFeature.INDENT_OUTPUT); om.enableDefaultTypingAsProperty(DefaultTyping.NON_FINAL, "@class"); Process p = new Process(); Scope s = new Scope(p, null); FaultHandler fh = new FaultHandler(p); Catch c = new Catch(p, s); fh.catchBlocks.add(c); s.faultHandlers.add(fh); String json = om.writeValueAsString(p); Process restored = om.readValue(json, Process.class); assertNotNull(restored); assertEquals(0, p.id); assertEquals(3, p.children.size()); assertSame(p, p.children.get(0).owner); assertSame(p, p.children.get(1).owner); assertSame(p, p.children.get(2).owner); } }