【问题标题】:Jackson - Deserialize Generic class variableJackson - 反序列化通用类变量
【发布时间】:2012-07-24 11:33:32
【问题描述】:

我错误地发布了问题。我在这里正确地发布了问题......

我得到一个 json 字符串作为 HTTP 响应。我知道它的结构。如下:

public class Json<T> {
    public Hits<T> hits;
}
public class Hits<T> {
    public int found;
    public int start;
    public ArrayList<Hit<T>> hit;
}
public class Hit<T> {
    public String id;
    public Class<T> data;
}

“数据”字段可以属于任何类。我只会在运行时知道它。我会把它作为参数。这就是我反序列化的方式。

public <T> void deSerialize(Class<T> clazz) {
    ObjectMapper mapper = new ObjectMapper();
    mapper.readValue(jsonString,  new TypeReference<Json<T>>() {});
}

但我收到一个错误 -

无法从 java.lang.class 访问私有 java.lang.class.Class()。未能设置访问权限。无法使 java.lang.Class 构造函数可访问

【问题讨论】:

标签: java json jackson generics


【解决方案1】:

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

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

public class Json<T extends Result> {
    public Hits<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 讨论了多态反序列化,但对于这个问题也很有用。

【讨论】:

    【解决方案2】:

    通用反序列化接口示例:

    public interface Deserializable<T> {
        static final ObjectMapper mapper = new ObjectMapper();
    
        @SuppressWarnings("unchecked")
        default T deserialize(String rawJson) throws IOException {
            return mapper.readValue(rawJson, (Class<T>) this.getClass());
        }
    }
    

    【讨论】:

    • 我喜欢这个答案,至少就我而言。我有很多类,它们都应该有一个 read-from-json 方法。因此,我编写了一个类型为 R 的基类,在读取类中我偶然发现了同样的问题:readValue(file, R.class)???当然这不起作用,因为类型在编译期间被删除了,那该怎么办呢? this.getClass() 是显而易见的答案。
    【解决方案3】:

    如果泛型类型仅动态可用,则需要显式构建JavaType

    // do NOT create new ObjectMapper per each request!
    final ObjectMapper mapper = new ObjectMapper();
    
    public Json<T> void deSerialize(Class<T> clazz, InputStream json) {
        return mapper.readValue(json,
          mapper.getTypeFactory().constructParametricType(Json.class, clazz));
    }
    

    【讨论】:

    • 根据我的经验,这不起作用,但它可能是一个错误:github.com/FasterXML/jackson-databind/issues/254
    • 引用的错误还有其他问题——如上所示的泛型类型解析将起作用,但不能解决其他可能的问题。
    • 是的,很抱歉,这里已经解决了stackoverflow.com/questions/17400850/…
    • 如果我有几个泛型类型,比如 ``` class MyClass { T1 someValue; T2 someValue2; } ```
    • @maks1m 然后你需要按参数数量分开的方法
    【解决方案4】:

    反序列化通用类变量

    ...

    我该如何告诉杰克逊? gson 会做得更好吗?

    The Gson user guide includes a section 正是我理解您正在尝试完成的事情,尽管该记录的示例可能仍然不完整。

    In a blog post, I covered in more detail the solution using Gson 1.7.1.下面是相关代码示例。

    使用 Jackson (1.8.2) 的类似(但涉及更多)解决方案也在同一篇博文中进行了演示和描述。 (不同的方法和示例使用了数百行代码,所以我在这里省略了重新发布。)

    public class GsonInstanceCreatorForParameterizedTypeDemo  
    {  
     public static void main(String[] args)  
     {  
      Id<String> id1 = new Id<String>(String.class, 42);  
      
      Gson gson = new GsonBuilder().registerTypeAdapter(Id.class,  
        new IdInstanceCreator()).create();  
      String json1 = gson.toJson(id1);  
      System.out.println(json1);  
      // actual output: {"classOfId":{},"value":42}  
      // This contradicts what the Gson docs say happens.  
      // With custom serialization, as described in a  
      // previous Gson user guide section,   
      // intended output may be   
      // {"value":42}  
      
      // input: {"value":42}  
      String json2 = "{\"value\":42}";  
      
      Type idOfStringType=new TypeToken<Id<String>>(){}.getType();  
      Id<String> id1Copy = gson.fromJson(json2, idOfStringType);  
      System.out.println(id1Copy);  
      // output: classOfId=class java.lang.String, value=42  
      
      Type idOfGsonType = new TypeToken<Id<Gson>>() {}.getType();  
      Id<Gson> idOfGson = gson.fromJson(json2, idOfGsonType);  
      System.out.println(idOfGson);  
      // output: classOfId=class com.google.gson.Gson, value=42  
     }  
    }  
      
    class Id<T>  
    {  
     private final Class<T> classOfId;  
     private final long value;  
      
     public Id(Class<T> classOfId, long value)  
     {  
      this.classOfId = classOfId;  
      this.value = value;  
     }  
      
     @Override  
     public String toString()  
     {  
      return "classOfId=" + classOfId + ", value=" + value;  
     }  
    }  
      
    class IdInstanceCreator implements InstanceCreator<Id<?>>  
    {  
     @SuppressWarnings({ "unchecked", "rawtypes" })  
     public Id<?> createInstance(Type type)  
     {  
      Type[] typeParameters =   
        ((ParameterizedType) type).getActualTypeArguments();  
      Type idType = typeParameters[0];  
      return new Id((Class<?>) idType, 0L);  
     }  
    } 
    

    【讨论】:

      【解决方案5】:

      您正在将 Class 对象序列化和反序列化为 JSON?也许在 Hit 中将其保留为 String 并创建启动 Class.forName 的额外 getter,例如

      public class Hit {
          public String id;
          public String data;
          public Class<?> getDataClass() throws Exception {
             return Class.forName(data);
          }
      }
      

      【讨论】:

      • 你得到的错误是:“我想instatinate新的Class对象,但我不能执行new Class(),因为构造函数是私有的”。更何况这毫无意义。请放置一个示例 JSON 字符串。那时提出解决方案会容易得多。我很确定 Class 类型的序列化/反序列化属性是错误的路径。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-08-14
      • 1970-01-01
      • 1970-01-01
      • 2016-08-05
      • 2017-11-06
      • 1970-01-01
      相关资源
      最近更新 更多