我们有一些JSON-POJO具有复杂的Type层次结构,并且面临类型注释的问题:
小例子:
SomeObjectA -> ObjectA -> AbstractObject
ObjectContainer
仅限AbstractObject
(仅ObjectA
或ObjectB
,永不SomeObjectA
)SomeObjectContainer
仅限SomeObjectA
在我们的实现中,AbstractObject
将是一些Geo-JSON-Object。 SomeObjectA
会扩展Geo-JSON(使用自定义参数而非使用properties
地图),但必须与type
具有相同的ObjectA
。
我们正在寻找正确/最好的方式来注释AbstractObject
以满足我们的需求并与杰克逊一起使用。
版本1:仅使用JsonTypeInfo
:
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({ //
@Type(value = ObjectA.class, name = "MY_OBJECT_A"), //
@Type(value = ObjectB.class, name = "MY_OBJECT_B") })
public abstract class AbstractObject {
public enum Type {
MY_OBJECT_A, MY_OBJECT_B
}
public Type type;
}
public class ObjectA extends AbstractObject {
public ObjectA() {
type = Type.MY_OBJECT_A;
}
public String a;
}
public class ObjectB extends AbstractObject {
public ObjectB() {
type = Type.MY_OBJECT_B;
}
public String b;
}
public class ObjectContainer {
public AbstractObject object;
}
public class SomeObjectA extends ObjectA {
public String value;
}
public class SomeObjectContainer {
public SomeObjectA object;
}
试验:
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import org.junit.Test;
import com.test.TestUtil;
public class ObjectATest {
@Test
public void test() throws IOException {
ObjectA object = new ObjectA();
String json = TestUtil.OBJECT_MAPPER.writeValueAsString(object);
System.out.println("json: " + json);
ObjectA actual = TestUtil.OBJECT_MAPPER.readValue(json, ObjectA.class);
System.out.println("actual: " + actual + "\n" + TestUtil.OBJECT_MAPPER.writeValueAsString(actual));
assertEquals("duplicate type", -1, json.indexOf("type", json.indexOf("type") + 4));
}
}
public class ObjectContainerTest {
@Test
public void test() throws IOException {
ObjectContainer container = new ObjectContainer();
container.object = new ObjectA();
((ObjectA) container.object).a = "test-a";
String json = TestUtil.OBJECT_MAPPER.writeValueAsString(container);
System.out.println("json: " + json);
ObjectContainer actual = TestUtil.OBJECT_MAPPER.readValue(json, ObjectContainer.class);
System.out.println("actual: " + actual + "\n" + TestUtil.OBJECT_MAPPER.writeValueAsString(actual));
assertEquals("test-a", ((ObjectA) actual.object).a);
assertEquals(ObjectA.class, actual.object.getClass());
assertEquals("duplicate type", -1, json.indexOf("type", json.indexOf("type") + 4));
}
}
public class SomeObjectContainerTest {
@Test
public void test() throws IOException {
SomeObjectContainer container = new SomeObjectContainer();
container.object = new SomeObjectA();
container.object.a = "test-a";
container.object.value = "test-value";
String json = TestUtil.OBJECT_MAPPER.writeValueAsString(container);
System.out.println("json: " + json);
SomeObjectContainer actual = TestUtil.OBJECT_MAPPER.readValue(json, SomeObjectContainer.class);
System.out.println("actual: " + actual + "\n" + TestUtil.OBJECT_MAPPER.writeValueAsString(actual));
assertEquals(container.object.value, actual.object.value);
assertEquals("duplicate type", -1, json.indexOf("type", json.indexOf("type") + 4));
}
}
测试输出和结果:
ObjectATest:
json: {
"type" : "MY_OBJECT_A",
"type" : "MY_OBJECT_A"
}
FAILURE: java.lang.AssertionError: duplicate type expected:<-1> but was:<33>
ObjectContainerTest:
json: {
"object" : {
"type" : "MY_OBJECT_A",
"type" : "MY_OBJECT_A",
"a" : "test-a"
}
}
FAILURE: java.lang.AssertionError: duplicate type expected:<-1> but was:<53>
SomeObjectContainerTest:
json: {
"object" : {
"type" : "SomeObjectA",
"type" : "MY_OBJECT_A",
"a" : "test-a",
"value" : "test-value"
}
}
FAILURE: java.lang.AssertionError: duplicate type expected:<-1> but was:<53>
版本1似乎有效,但会产生重复的type
参数。 - 这可能会打破一些客户......?
版本2:使用JsonTypeInfo
和JsonTypeId
:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({ //
@Type(value = ObjectA.class, name = "MY_OBJECT_A"), //
@Type(value = ObjectB.class, name = "MY_OBJECT_B") })
public abstract class AbstractObject {
public enum Type {
MY_OBJECT_A, MY_OBJECT_B
}
@JsonTypeId
public Type type;
}
测试输出和结果:
ObjectATest:
json: {
"type" : "MY_OBJECT_A"
}
SUCCESS
ObjectContainerTest:
json: {
"object" : {
"type" : "MY_OBJECT_A",
"a" : "test-a"
}
}
SUCCESS
SomeObjectContainerTest:
json: {
"object" : {
"type" : "MY_OBJECT_A",
"a" : "test-a",
"value" : "test-value"
}
}
ERROR: com.fasterxml.jackson.databind.JsonMappingException: Class com.test2.ObjectA not subtype of [simple type, class com.test2.SomeObjectA] (through reference chain: com.test2.SomeObjectContainer["object"])
版本2无法阅读SomeObjectContainer
。
我们也尝试使用以下注释:
@JsonSubTypes({ //
@Type(value = ObjectA.class, name = "MY_OBJECT_A"), //
@Type(value = SomeObjectA.class, name = "MY_OBJECT_A"), //
@Type(value = ObjectB.class, name = "MY_OBJECT_B") })
但这个没有帮助。
为什么杰克逊不能阅读SomeObjectContainer
? - object
必须是SomeObjectA
(或其某些子类),永远不会是SomeObjectA
或ObjectA
!
如何解决这个问题?
是否有另一个注释放在SomeObjectContainer.object
?