杰克逊-使用泛型类反序列化


Answers:


238

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

mapper.readValue(jsonString, new TypeReference<Data<String>>() {});

我必须将其用作TypeReference <Data <T >>(){} ...但是我遇到以下错误-无法从java.lang.class访问私有java.lang.class.Class()。设置访问权限失败。无法使java.lang.Class构造函数可访问
gnjago

不,不是Data<T>,那不是一种类型。您必须指定实际班级;否则与相同Data<Object>
StaxMan 2012年

18
如果直到运行时我都不知道它是什么类,该怎么办?我将在运行时将类作为参数获取。像这样的公共<T> void deSerialize(Class <T> clazz {ObjectMapper mapper = new ObjectMapper(); mapper.readValue(jsonString,new TypeReference <Json <T >>(){});}
gnjago 2012年

1
我也问过满问题正确这里stackoverflow.com/questions/11659844/...
gnjago

的完整包裹名称是TypeReference什么?是com.fasterxml.jackson.core.type
杨雷

88

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

但是,如果您想T知道的只是静态的,则需要创建TypeReference动态等效项。引用的其他问题可能已经提到了这一点,但它看起来应该像这样:

public Data<T> read(InputStream json, Class<T> contentClass) {
   JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, contentClass);
   return mapper.readValue(json, type);
}

2
如果直到运行时我都不知道它是什么类,该怎么办?我将在运行时将类作为参数获取。像这样的公共<T> void deSerialize(Class <T> clazz {ObjectMapper mapper = new ObjectMapper(); mapper.readValue(jsonString,new TypeReference <Json <T >>(){});}
gnjago 2012年

1
我也问过这里的全部问题stackoverflow.com/questions/11659844/...
gnjago

2
然后,按原样传递类,无需TypeReferencereturn mapper.readValue(json, clazz);在这里到底是什么问题?
StaxMan 2012年

2
问题在于“数据”是一个通用类。我需要在运行时指定T类型。参数clazz是运行时的T us。那么,如何调用readValue?新TypeReference调用它>的Json <T >>不起作用完整的问题在这里stackoverflow.com/questions/11659844/...
gnjago

2
好。然后,您需要使用TypeFactory..我将编辑我的答案。
StaxMan 2012年

30

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

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

然后,当您反序列化时,您会发现杰克逊将数据反序列化为一个类,您的变量命中实际上是在运行时。


不起作用,出现以下错误com.fasterxml.jackson.databind.exc.InvalidTypeIdException:尝试解析[简单类型,类java.lang.Object]的子类型时,缺少类型ID:缺少类型ID属性'@class'(用于POJO)属性“数据”)
gaurav9620

15

对于类Data <>

ObjectMapper mapper = new ObjectMapper();
JavaType type = mapper.getTypeFactory().constructParametrizedType(Data.class, Data.class, Parameter.class);
Data<Parameter> dataParam = mapper.readValue(jsonString,type)

现在不建议使用。
Evan Gertis

13

只需在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);

7

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

例如,

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

这里的东西是具体的类型。您需要为每种类型化包装。否则,杰克逊不知道要创建什么对象。


6

需要反序列化的JSON字符串将必须包含有关parameter的类型信息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 {

}

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


如果要有清单,该如何管理?就像说List <ImageResult>
Luca Archidiacono

5

从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);

太好了,谢谢,它对我有用。使用类型引用,我从映射到特定对象得到了类广播异常,但这确实可以完成工作。
Tacsiazuma

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;
    }
    ...
 }

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)

0

要使用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


0

示例不是很好,而是简单的决定(不仅适用于Jackson,还适用于Spring RestTemplate等):

Set<MyClass> res = new HashSet<>();
objectMapper.readValue(json, res.getClass());
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.