【问题标题】:Jackson - Deserialize using generic classJackson - 使用泛型类反序列化
【发布时间】:2012-07-24 18:42:47
【问题描述】:

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

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

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

mapper.readValue(jsonString, Data.class);

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

【问题讨论】:

标签: java json generics jackson


【解决方案1】:

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

例如,

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

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 我必须将它用作 TypeReference>(){} ...但我收到以下错误 - 无法从 java 访问私有 java.lang.class.Class() .lang.class。未能设置访问权限。无法使 java.lang.Class 构造函数可访问
    • 不,不是Data&lt;T&gt;,那不是一个类型。您必须指定实际班级;否则与Data&lt;Object&gt;相同。
    • 如果直到运行时我才知道它是什么类怎么办?我将在运行时将类作为参数获取。像这个 public void deSerialize(Class clazz { ObjectMapper mapper = new ObjectMapper(); mapper.readValue(jsonString, new TypeReference>() {}); }
    • 我已经在这里正确地问了完整的问题stackoverflow.com/questions/11659844/…
    • TypeReference 的完整包名是什么?是com.fasterxml.jackson.core.type吗?
    【解决方案3】:

    您不能这样做:您必须指定完全解析的类型,例如Data&lt;MyType&gt;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);
    }
    

    【讨论】:

    • 如果直到运行时我才知道它是什么类怎么办?我将在运行时将类作为参数获取。像这个 public void deSerialize(Class clazz { ObjectMapper mapper = new ObjectMapper(); mapper.readValue(jsonString, new TypeReference>() {}); }
    • 那么就照原样传课,不需要TypeReferencereturn mapper.readValue(json, clazz);这到底是什么问题?
    • 问题是“数据”是一个泛型类。我需要在运行时指定 T 是什么类型。参数 clazz 是我们在运行时使用的。那么,如何调用 readValue 呢?用新的 TypeReference>Json> 调用它不起作用完整的问题在这里stackoverflow.com/questions/11659844/…
    • 好的。然后你需要使用TypeFactory.. 我会编辑我的答案。
    • 在 Jackson 2.5 及更高版本中,您应该使用 TypeFactory.constructParametrizedType,因为 TypeFactory. constructParametricType 已弃用。
    【解决方案4】:

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

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

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

    【讨论】:

    • 不工作,低于错误 com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id属性“@class”(用于 POJO 属性“数据”)
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

      需要反序列化的JSON字符串必须包含参数T的类型信息。
      您必须在每个可以作为参数T 传递给类Data 的类上放置Jackson 注释,以便Jackson 可以从/写入JSON 字符串中读取/写入有关参数类型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 将在 JSON 中包含有关参数T 的信息。然后可以在编译时不知道参数T 的情况下反序列化此类 JSON。
      这个Jackson documentation link 讨论了多态反序列化,但对于这个问题也很有用。

      【讨论】:

      • 如果我想要一个列表,我该如何管理?比如说 List
      【解决方案7】:

      对于数据类

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

      【讨论】:

      • 现在已弃用。
      【解决方案8】:

      如果您使用 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)
      

      【讨论】:

        【解决方案9】:

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

        假设你想反序列化为Data&lt;String&gt;,你可以这样做:

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

        【讨论】:

        • 太棒了,谢谢它对我有用。使用类型引用,我得到了从映射到特定对象的类转换异常,但这确实可以完成工作。
        【解决方案10】:
        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;
            }
            ...
         }
        

        【讨论】:

          【解决方案11】:

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

          【讨论】:

            猜你喜欢
            • 2012-10-18
            • 1970-01-01
            • 2019-04-29
            • 2014-04-05
            • 1970-01-01
            • 2016-05-27
            • 1970-01-01
            相关资源
            最近更新 更多