206

我有一个 json 字符串,我应该将其反序列化为以下类

class Data <T> {
    int found;
    Class<T> hits
}

我该怎么做?这是通常的方式

mapper.readValue(jsonString, Data.class);

但是我该如何提及 T 代表什么?

4

11 回答 11

338

您需要为TypeReference您使用的每个泛型类型创建一个对象,并将其用于反序列化。例如 -

mapper.readValue(jsonString, new TypeReference<Data<String>>() {});
于 2012-07-26T11:49:19.193 回答
106

你不能这样做:你必须指定完全解析的类型,比如Data<MyType>. T只是一个变量,as没有意义。

但是,如果您的意思T是已知的,而不是静态的,则需要TypeReference动态地创建等效的。引用的其他问题可能已经提到了这一点,但它应该类似于:

public Data<T> read(InputStream json, Class<T> contentClass) {
   JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, contentClass);
   return mapper.readValue(json, type);
}
于 2012-07-27T04:39:43.283 回答
37

你要做的第一件事是序列化,然后你可以反序列化。
因此,当您进行序列化时,您应该使用@JsonTypeInfo让杰克逊将类信息写入您的 json 数据。你可以做的是这样的:

Class Data <T> {
    int found;
    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
    Class<T> hits
}

然后,当您反序列化时,您会发现 jackson 已将您的数据反序列化为一个类,您的变量在运行时实际命中该类。

于 2013-01-07T10:06:28.433 回答
24

从 Jackson 2.5 开始,一种优雅的解决方法是使用 TypeFactory.constructParametricType(Class parametrized, Class... parameterClasses)JavaType方法,该方法允许通过指定参数化类及其参数化类型 来直接定义 Jackson 。

假设你想反序列化到Data<String>,你可以这样做:

// the json variable may be a String, an InputStream and so for...
JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, String.class);
Data<String> data = mapper.readValue(json, type);

请注意,如果该类声明了多个参数化类型,则不会更难:

class Data <T, U> {
    int found;
    Class<T> hits;
    List<U> list;
}

我们可以这样做:

JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, String.class, Integer);
Data<String, Integer> data = mapper.readValue(json, type);
于 2019-07-16T21:44:41.513 回答
15

For class Data<>

ObjectMapper mapper = new ObjectMapper();
JavaType type = mapper.getTypeFactory().constructParametrizedType(Data.class, Data.class, Parameter.class);
Data<Parameter> dataParam = mapper.readValue(jsonString,type)
于 2018-03-16T18:28:38.277 回答
14

只需在 Util 类中编写一个静态方法。我正在从文件中读取 Json。您也可以将 String 提供给 readValue

public static <T> T convertJsonToPOJO(String filePath, Class<?> target) throws JsonParseException, JsonMappingException, IOException, ClassNotFoundException {
        ObjectMapper objectMapper = new ObjectMapper();
        return objectMapper.readValue(new File(filePath), objectMapper .getTypeFactory().constructCollectionType(List.class, Class.forName(target.getName())));
}

用法:

List<TaskBean> list =  Util.<List<TaskBean>>convertJsonToPOJO("E:/J2eeWorkspaces/az_workspace_svn/az-client-service/dir1/dir2/filename.json", TaskBean.class);
于 2013-10-09T03:59:22.590 回答
7

您可以将它包装在另一个知道泛型类型的类中。

例如,

class Wrapper {
 private Data<Something> data;
}
mapper.readValue(jsonString, Wrapper.class);

这里Something是一个具体的类型。每个具体类型都需要一个包装器。否则杰克逊不知道要创建什么对象。

于 2012-07-26T11:32:38.083 回答
7

需要反序列化的 JSON 字符串必须包含有关参数的类型信息T
您必须在可以作为参数传递T给类的每个类上放置 Jackson 注释,以便 Jackson可以从 JSON 字符串读取/写入Data有关参数类型的类型信息。T

让我们假设它T可以是任何扩展抽象类的类Result

class Data <T extends Result> {
    int found;
    Class<T> hits
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
@JsonSubTypes({
        @JsonSubTypes.Type(value = ImageResult.class, name = "ImageResult"),
        @JsonSubTypes.Type(value = NewsResult.class, name = "NewsResult")})
public abstract class Result {

}

public class ImageResult extends Result {

}

public class NewsResult extends Result {

}

一旦可以作为参数传递的每个类(或其公共超类型)都被注释,Jackson 将在 JSON 中T 包含有关参数的信息。T然后可以T在编译时不知道参数的情况下反序列化此类 JSON。
这个Jackson 文档链接讨论了多态反序列化,但对于这个问题也很有用。

于 2018-02-20T08:10:42.203 回答
1
public class Data<T> extends JsonDeserializer implements ContextualDeserializer {
    private Class<T> cls;
    public JsonDeserializer createContextual(DeserializationContext ctx, BeanProperty prop) throws JsonMappingException {
        cls = (Class<T>) ctx.getContextualType().getRawClass();
        return this;
    }
    ...
 }
于 2019-09-05T12:13:27.177 回答
0

如果您使用 scala 并且在编译时知道泛型类型,但不想在所有 api 层中的任何地方手动传递 TypeReference,您可以使用以下代码(使用 jackson 2.9.5):

def read[T](entityStream: InputStream)(implicit typeTag: WeakTypeTag[T]): T = {

    //nathang: all of this *crazy* scala reflection allows us to handle List[Seq[Map[Int,Value]]]] without passing
    // new TypeReference[List[Seq[Map[Int,Value]]]]](){} to the function

    def recursiveFindGenericClasses(t: Type): JavaType = {
      val current = typeTag.mirror.runtimeClass(t)

      if (t.typeArgs.isEmpty) {
        val noSubtypes = Seq.empty[Class[_]]
        factory.constructParametricType(current, noSubtypes:_*)
      }

      else {
        val genericSubtypes: Seq[JavaType] = t.typeArgs.map(recursiveFindGenericClasses)
        factory.constructParametricType(current, genericSubtypes:_*)
      }

    }

    val javaType = recursiveFindGenericClasses(typeTag.tpe)

    json.readValue[T](entityStream, javaType)
  }

可以这样使用:

read[List[Map[Int, SomethingToSerialize]]](inputStream)
于 2018-04-24T09:49:43.183 回答
-1

要使用 Jackson 将通用 JSON 字符串反序列化为 Java 对象,您需要:

  1. 定义一个 JSON 类。

  2. 执行属性映射。

最终代码,经过测试,可以使用:

static class MyJSON {

    private Map<String, Object> content = new HashMap<>();

    @JsonAnySetter
    public void setContent(String key, Object value) {
        content.put(key, value);
    }
}

String json = "{\"City\":\"Prague\"}";

try {

    MyPOJO myPOJO = objectMapper.readValue(json, MyPOJO.class);

    String jsonAttVal = myPOJO.content.get("City").toString();

    System.out.println(jsonAttVal);

} catch (IOException e) {
    e.printStackTrace();
}

重要提示:
@JsonAnySetter注释是强制性的,它确保了通用的 JSON 解析和填充。

对于嵌套数组的更复杂情况,请参阅 Baeldung 参考: https ://www.baeldung.com/jackson-mapping-dynamic-object

于 2020-03-11T14:01:54.413 回答