3

我正在尝试使用 Jakson 反序列化嵌套的多态类型。这意味着我的顶级类型是指另一种多态类型,最终由非抽象类扩展。这不起作用,它会引发异常。

这是我正在尝试做的一个简化示例。

package com.adfin;

import junit.framework.TestCase;
import org.codehaus.jackson.annotate.JsonSubTypes;
import org.codehaus.jackson.annotate.JsonTypeInfo;
import org.codehaus.jackson.map.ObjectMapper;

import java.io.IOException;

public class JaksonDouble extends TestCase {

  @JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "name"
  )
  @JsonSubTypes({
    @JsonSubTypes.Type(value = SecondLevel.class, name = "SECOND")
  })
  public static abstract class FirstLevel {
    public abstract String getTestValue();
  }

  @JsonTypeInfo(
    use = JsonTypeInfo.Id.CLASS,
    include = JsonTypeInfo.As.PROPERTY,
    property = "@class"
  )
  public static abstract class SecondLevel extends FirstLevel {

  }

  public static class FinalLevel extends SecondLevel {
    String test;
    @Override public String getTestValue() { return test; }
  }

  public void testDoubleAbstract() throws IOException {
    String testStr = "{ \"name\": \"SECOND\", \"@class\": \"com.adfin.JasksonDouble.FinalLevel\", \"test\": \"foo\"}";

    ObjectMapper mapper = new ObjectMapper();
    FirstLevel result = mapper.readValue(testStr, FirstLevel.class);
  }
}

我得到了关于抽象类型的标准例外。

org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.adfin.JaksonDouble$SecondLevel, problem: abstract types can only be instantiated with additional type information at [Source: java.io.StringReader@f2a55aa; line: 1, column: 19]

让我解释一下我的用例。我有一个描述数据工作流程的 Json 文档。我在“一级”有一个抽象类型,描述了对单个值的操作。我派生了一堆实现常见操作的非抽象类(我用@JsonSubTypes 对它们进行了注释)。

我有一个特殊的@JsonSubTypes,称为“CUSTOM”。这是另一个抽象类,代表其他人(在普通 jar 之外)编写的自定义操作,他们可以使用“@class”属性指定完全限定的类名。看起来 Jakson 解析器从不读取第二个 lavel 类上的 @JsonTypeInfo 注释。

我怎样才能使这项工作。或者至少我怎样才能使这个用例起作用。

4

2 回答 2

2

你的定义搞砸了——你试图使用两个类型标识符,类型名称和类。这没有任何意义。您应该选择一种方法或另一种方法,而不是两者兼而有之。

如果您选择 Java 类名作为类型信息,只需省略名称即可。此外,您只需要包含@JsonTypeInfofor FirstLevel; 子类继承这个定义。

如果您更喜欢使用逻辑类型名称,请删除类属性。您还需要指定子类型列表,可以使用注释,也可以通过ObjectMapper.

于 2013-01-09T04:15:54.580 回答
0

首先,你的 json 中的类名是错误的,它应该是 com.adfin.JasksonDouble$FinalLevel,美元而不是点。

这是一个有效的代码,但我不确定它是否能正确回答所有子类型的内容。

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
public static abstract class FirstLevel {
    public abstract String getTestValue();
}

从其他类中删除注释,它应该可以工作(刚刚测试过)。

然而,所有这些东西看起来都很复杂,如果你可以自由地尝试另一个库,你可能想看看Genson。要启用多态类型支持,您必须配置您的 Genson 实例。如果您也是生成 json 的人,则不需要其他任何东西(因为您可以根据需要使用 Genson 生成 json 流来处理多态类型)。

这是一个例子:

// enable polymorphic types support
Genson genson = new Genson.Builder().setWithClassMetadata(true).create();
// the @class must be first property in the json object
String testStr = "{ \"@class\": \"com.adfin.JasksonDouble$FinalLevel\", \"test\": \"foo\"}";
FirstLevel result = genson.deserialize(testStr, FirstLevel.class);
System.out.println(result.getTestValue());

Genson 的另一个好处是它使您能够为您的类注册别名,因此您不必在流中提供所有包信息。另一个优点是,如果您将 json 流存储在数据库中并将您的类移动到另一个包中,您只需更改应用程序中的别名,而不需要从数据库中迁移所有 json 流。

Genson genson = new Genson.Builder().setWithClassMetadata(true)
                                    .addAlias("FinalLevel", FinalLevel.class)
                                    .create();
// the @class must be first property in the json object
String testStr = "{ \"@class\": \"FinalLevel\", \"test\": \"foo\"}";
FirstLevel result = genson.deserialize(testStr, FirstLevel.class);
System.out.println(result.getTestValue());
于 2013-01-08T23:48:11.493 回答