反序列化列表对象与Gson?

发布于 2024-10-30 16:36:38 字数 2639 浏览 2 评论 0 原文

我想通过 Google Gson 传输列表对象,但我不知道如何反序列化泛型类型。

我在查看这个后尝试了什么( BalusC 的回答):

MyClass mc = new Gson().fromJson(result, new List<MyClass>() {}.getClass());

但随后我在 Eclipse 中收到错误消息“The type new List() {} 必须实现继承的抽象方法...”,如果我使用快速修复,我获得一个拥有超过 20 个方法存根的怪物。

我很确定有一个更简单的解决方案,但我似乎找不到它!

现在我有这个:

Type listType = new TypeToken<List<MyClass>>() {}.getType();

MyClass mc = new Gson().fromJson(result, listType);

但是,我确实在 fromJson 行收到以下异常:

java.lang.NullPointerException
at org.apache.harmony.luni.lang.reflect.ListOfTypes.length(ListOfTypes.java:47)
at org.apache.harmony.luni.lang.reflect.ImplForType.toString(ImplForType.java:83)
at java.lang.StringBuilder.append(StringBuilder.java:203)
at com.google.gson.JsonDeserializerExceptionWrapper.deserialize(JsonDeserializerExceptionWrapper.java:56)
at com.google.gson.JsonDeserializationVisitor.invokeCustomDeserializer(JsonDeserializationVisitor.java:88)
at com.google.gson.JsonDeserializationVisitor.visitUsingCustomHandler(JsonDeserializationVisitor.java:76)
at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:106)
at com.google.gson.JsonDeserializationContextDefault.fromJsonArray(JsonDeserializationContextDefault.java:64)
at com.google.gson.JsonDeserializationContextDefault.deserialize(JsonDeserializationContextDefault.java:49)
at com.google.gson.Gson.fromJson(Gson.java:568)
at com.google.gson.Gson.fromJson(Gson.java:515)
at com.google.gson.Gson.fromJson(Gson.java:484)
at com.google.gson.Gson.fromJson(Gson.java:434)

do catch JsonParseExceptionsresult 不为空。

我使用调试器检查了 listType 并得到以下结果:

  • list Type
    • args = ListOfTypes
      • 列表= null
      • resolvedTypes = 类型[1]
    • loader = PathClassLoader
    • ownerType0 = null
    • ownerTypeRes = null
    • rawType = 类 (java.util.ArrayList)
    • rawTypeName = "java.util.ArrayList"

所以看来 getClass 调用没有无法正常工作。有什么建议吗...?

我已查看Gson 用户指南。它提到了将泛型类型解析为 Json 期间应该发生的运行时异常。正如示例中所示,我做错了(上面未显示),但根本没有得到该异常。所以我按照用户指南的建议更改了序列化。但没有帮助。

编辑:

已解决,请参阅下面的答案。

I want to transfer a list object via Google Gson, but I don't know how to deserialize generic types.

What I tried after looking at this (BalusC's answer):

MyClass mc = new Gson().fromJson(result, new List<MyClass>() {}.getClass());

but then I get an error in Eclipse saying "The type new List<MyClass>() {} must implement the inherited abstract method..." and if I use a quick fix I get a monster of over 20 method stubs.

I am pretty sure that there is an easier solution, but I seem unable to find it!

Now I have this:

Type listType = new TypeToken<List<MyClass>>() {}.getType();

MyClass mc = new Gson().fromJson(result, listType);

However, I do get the following exception at the fromJson line:

java.lang.NullPointerException
at org.apache.harmony.luni.lang.reflect.ListOfTypes.length(ListOfTypes.java:47)
at org.apache.harmony.luni.lang.reflect.ImplForType.toString(ImplForType.java:83)
at java.lang.StringBuilder.append(StringBuilder.java:203)
at com.google.gson.JsonDeserializerExceptionWrapper.deserialize(JsonDeserializerExceptionWrapper.java:56)
at com.google.gson.JsonDeserializationVisitor.invokeCustomDeserializer(JsonDeserializationVisitor.java:88)
at com.google.gson.JsonDeserializationVisitor.visitUsingCustomHandler(JsonDeserializationVisitor.java:76)
at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:106)
at com.google.gson.JsonDeserializationContextDefault.fromJsonArray(JsonDeserializationContextDefault.java:64)
at com.google.gson.JsonDeserializationContextDefault.deserialize(JsonDeserializationContextDefault.java:49)
at com.google.gson.Gson.fromJson(Gson.java:568)
at com.google.gson.Gson.fromJson(Gson.java:515)
at com.google.gson.Gson.fromJson(Gson.java:484)
at com.google.gson.Gson.fromJson(Gson.java:434)

I do catch JsonParseExceptions and result is not null.

I checked listType with the debugger and got the following:

  • list Type
    • args = ListOfTypes
      • list = null
      • resolvedTypes = Type[ 1 ]
    • loader = PathClassLoader
    • ownerType0 = null
    • ownerTypeRes = null
    • rawType = Class (java.util.ArrayList)
    • rawTypeName = "java.util.ArrayList"

So it seems the getClass invocation didn't work properly. Any suggestions...?

I've checked on the Gson User Guide. It mentions a runtime exception that should happen during parsing a generic type to Json. I did it "wrong" (not shown above), just as in the example, but didn't get that exception at all. So I changed the serialization as in the user guide suggested. Didn't help, though.

Edit:

Solved, see my answer below.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

迷爱 2024-11-06 16:36:39

反序列化泛型集合的方法:

import java.lang.reflect.Type;
import com.google.gson.reflect.TypeToken;

...

Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();
List<YourClass> yourClassList = new Gson().fromJson(jsonArray, listType);

由于评论中已经有好几个人提到了,这里解释一下TypeToken类的使用方法。构造 new TypeToken<...>() {}.getType() 捕获编译时类型(在 <>< 之间) /code>) 转换为运行时 java.lang.reflect.Type 对象。与只能表示原始(已删除)类型的 Class 对象不同,Type 对象可以表示 Java 语言中的任何类型,包括泛型类型的参数化实例化。

TypeToken 类本身没有公共构造函数,因为您不应该直接构造它。相反,您始终构造一个匿名子类(因此 {},它是该表达式的必要部分)。

由于类型擦除,TypeToken 类只能捕获在编译时完全已知的类型。 (也就是说,您不能对类型参数 T 执行 new TypeToken>() {}.getType()。)

了解更多信息,请参阅 TypeToken 的文档 类

Method to deserialize generic collection:

import java.lang.reflect.Type;
import com.google.gson.reflect.TypeToken;

...

Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();
List<YourClass> yourClassList = new Gson().fromJson(jsonArray, listType);

Since several people in the comments have mentioned it, here's an explanation of how the TypeToken class is being used. The construction new TypeToken<...>() {}.getType() captures a compile-time type (between the < and >) into a runtime java.lang.reflect.Type object. Unlike a Class object, which can only represent a raw (erased) type, the Type object can represent any type in the Java language, including a parameterized instantiation of a generic type.

The TypeToken class itself does not have a public constructor, because you're not supposed to construct it directly. Instead, you always construct an anonymous subclass (hence the {}, which is a necessary part of this expression).

Due to type erasure, the TypeToken class is only able to capture types that are fully known at compile time. (That is, you can't do new TypeToken<List<T>>() {}.getType() for a type parameter T.)

For more information, see the documentation for the TypeToken class.

停滞 2024-11-06 16:36:39

另一种方法是使用数组作为类型,例如:

MyClass[] mcArray = gson.fromJson(jsonString, MyClass[].class);

这样您就可以避免 Type 对象的所有麻烦,如果您确实需要一个列表,您可以随时将数组转换为列表:

List<MyClass> mcList = Arrays.asList(mcArray);

恕我直言,这更具可读性。

要使其成为一个实际列表(可以修改,请参阅 Arrays.asList() 的限制),然后只需执行以下操作:

List<MyClass> mcList = new ArrayList<>(Arrays.asList(mcArray));

Another way is to use an array as a type, e.g.:

MyClass[] mcArray = gson.fromJson(jsonString, MyClass[].class);

This way you avoid all the hassle with the Type object, and if you really need a list you can always convert the array to a list by:

List<MyClass> mcList = Arrays.asList(mcArray);

IMHO this is much more readable.

And to make it be an actual list (that can be modified, see limitations of Arrays.asList()) then just do the following:

List<MyClass> mcList = new ArrayList<>(Arrays.asList(mcArray));
ζ澈沫 2024-11-06 16:36:39

从 Gson 2.8 开始,我们可以像这样创建 util 函数:

public <T> List<T> getList(String jsonArray, Class<T> clazz) {
    Type typeOfT = TypeToken.getParameterized(List.class, clazz).getType();
    return new Gson().fromJson(jsonArray, typeOfT);
}

示例用法:

String jsonArray = ...
List<User> user = getList(jsonArray, User.class);

Since Gson 2.8, we can create util function like this:

public <T> List<T> getList(String jsonArray, Class<T> clazz) {
    Type typeOfT = TypeToken.getParameterized(List.class, clazz).getType();
    return new Gson().fromJson(jsonArray, typeOfT);
}

Example usage:

String jsonArray = ...
List<User> user = getList(jsonArray, User.class);
你是我的挚爱i 2024-11-06 16:36:39

参考这篇文章。
Java 类型泛型作为 GSON 的参数

我对此有更好的解决方案。这是列表的包装类,因此包装器可以存储列表的确切类型。

public class ListOfJson<T> implements ParameterizedType
{
  private Class<?> wrapped;

  public ListOfJson(Class<T> wrapper)
  {
    this.wrapped = wrapper;
  }

  @Override
  public Type[] getActualTypeArguments()
  {
      return new Type[] { wrapped };
  }

  @Override
  public Type getRawType()
  {
    return List.class;
  }

  @Override
  public Type getOwnerType()
  {
    return null;
  }
}

然后,代码可以很简单:

public static <T> List<T> toList(String json, Class<T> typeClass)
{
    return sGson.fromJson(json, new ListOfJson<T>(typeClass));
}

Refer to this post.
Java Type Generic as Argument for GSON

I have better solution for this. Here's the wrapper class for list so the wrapper can store the exactly type of list.

public class ListOfJson<T> implements ParameterizedType
{
  private Class<?> wrapped;

  public ListOfJson(Class<T> wrapper)
  {
    this.wrapped = wrapper;
  }

  @Override
  public Type[] getActualTypeArguments()
  {
      return new Type[] { wrapped };
  }

  @Override
  public Type getRawType()
  {
    return List.class;
  }

  @Override
  public Type getOwnerType()
  {
    return null;
  }
}

And then, the code can be simple:

public static <T> List<T> toList(String json, Class<T> typeClass)
{
    return sGson.fromJson(json, new ListOfJson<T>(typeClass));
}
凉城 2024-11-06 16:36:39

好吧,另一种方法可以达到相同的结果。我们使用它是因为它的可读性。

不要执行这个难以阅读的句子:

Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();
List<YourClass> list = new Gson().fromJson(jsonArray, listType);

创建一个扩展对象列表的空类:

public class YourClassList extends ArrayList<YourClass> {}

并在解析 JSON 时使用它:

List<YourClass> list = new Gson().fromJson(jsonArray, YourClassList.class);

Wep, another way to achieve the same result. We use it for its readability.

Instead of doing this hard-to-read sentence:

Type listType = new TypeToken<ArrayList<YourClass>>(){}.getType();
List<YourClass> list = new Gson().fromJson(jsonArray, listType);

Create a empty class that extends a List of your object:

public class YourClassList extends ArrayList<YourClass> {}

And use it when parsing the JSON:

List<YourClass> list = new Gson().fromJson(jsonArray, YourClassList.class);
小巷里的女流氓 2024-11-06 16:36:39

对于 Kotlin 来说,简单来说:

import java.lang.reflect.Type
import com.google.gson.reflect.TypeToken
...
val type = object : TypeToken<List<T>>() {}.type

或者,这里有一个有用的函数:

fun <T> typeOfList(): Type {
    return object : TypeToken<List<T>>() {}.type
}

然后,使用:

val type = typeOfList<YourMagicObject>()

For Kotlin simply:

import java.lang.reflect.Type
import com.google.gson.reflect.TypeToken
...
val type = object : TypeToken<List<T>>() {}.type

or, here is a useful function:

fun <T> typeOfList(): Type {
    return object : TypeToken<List<T>>() {}.type
}

Then, to use:

val type = typeOfList<YourMagicObject>()
失去的东西太少 2024-11-06 16:36:39
public static final <T> List<T> getList(final Class<T[]> clazz, final String json)
{
    final T[] jsonToObject = new Gson().fromJson(json, clazz);

    return Arrays.asList(jsonToObject);
}

例子:

getList(MyClass[].class, "[{...}]");
public static final <T> List<T> getList(final Class<T[]> clazz, final String json)
{
    final T[] jsonToObject = new Gson().fromJson(json, clazz);

    return Arrays.asList(jsonToObject);
}

Example:

getList(MyClass[].class, "[{...}]");
孤单情人 2024-11-06 16:36:39

由于它回答了我原来的问题,我已经接受了 doc_180 的答案,但是如果有人再次遇到这个问题,我也会回答我的问题的后半部分:

我描述的 NullPointerError 与 List 本身无关,但与其内容!

“MyClass”类没有“无参数”构造函数,它的超类也没有。一旦我向 MyClass 及其超类添加了一个简单的“MyClass()”构造函数,一切都工作正常,包括 doc_180 建议的列表序列化和反序列化。

As it answers my original question, I have accepted doc_180's answer, but if someone runs into this problem again, I will answer the 2nd half of my question as well:

The NullPointerError I described had nothing to do with the List itself, but with its content!

The "MyClass" class didn't have a "no args" constructor, and neither had its superclass one. Once I added a simple "MyClass()" constructor to MyClass and its superclass, everything worked fine, including the List serialization and deserialization as suggested by doc_180.

能否归途做我良人 2024-11-06 16:36:39

这是一个适用于动态定义类型的解决方案。诀窍是使用 Array.newInstance() 创建正确类型的数组。

public static <T> List<T> fromJsonList(String json, Class<T> clazz) {
    Object [] array = (Object[])java.lang.reflect.Array.newInstance(clazz, 0);
    array = gson.fromJson(json, array.getClass());
    List<T> list = new ArrayList<T>();
    for (int i=0 ; i<array.length ; i++)
        list.add(clazz.cast(array[i]));
    return list; 
}

Here is a solution that works with a dynamically defined type. The trick is creating the proper type of of array using Array.newInstance().

public static <T> List<T> fromJsonList(String json, Class<T> clazz) {
    Object [] array = (Object[])java.lang.reflect.Array.newInstance(clazz, 0);
    array = gson.fromJson(json, array.getClass());
    List<T> list = new ArrayList<T>();
    for (int i=0 ; i<array.length ; i++)
        list.add(clazz.cast(array[i]));
    return list; 
}
°如果伤别离去 2024-11-06 16:36:39

我想补充一种可能性。如果你不想使用 TypeToken 并且想要将 json 对象数组转换为 ArrayList,那么你可以像这样继续:

如果你的 json 结构是这样的:

{

"results": [
    {
        "a": 100,
        "b": "value1",
        "c": true
    },
    {
        "a": 200,
        "b": "value2",
        "c": false
    },
    {
        "a": 300,
        "b": "value3",
        "c": true
    }
]

}

并且你的类结构是这样的:

public class ClassName implements Parcelable {

    public ArrayList<InnerClassName> results = new ArrayList<InnerClassName>();
    public static class InnerClassName {
        int a;
        String b;
        boolean c;      
    }
}

那么你可以像这样解析它:

Gson gson = new Gson();
final ClassName className = gson.fromJson(data, ClassName.class);
int currentTotal = className.results.size();

现在您可以访问 className 对象的每个元素。

I want to add for one more possibility. If you don't want to use TypeToken and want to convert json objects array to an ArrayList, then you can proceed like this:

If your json structure is like:

{

"results": [
    {
        "a": 100,
        "b": "value1",
        "c": true
    },
    {
        "a": 200,
        "b": "value2",
        "c": false
    },
    {
        "a": 300,
        "b": "value3",
        "c": true
    }
]

}

and your class structure is like:

public class ClassName implements Parcelable {

    public ArrayList<InnerClassName> results = new ArrayList<InnerClassName>();
    public static class InnerClassName {
        int a;
        String b;
        boolean c;      
    }
}

then you can parse it like:

Gson gson = new Gson();
final ClassName className = gson.fromJson(data, ClassName.class);
int currentTotal = className.results.size();

Now you can access each element of className object.

岁月蹉跎了容颜 2024-11-06 16:36:39

Gson 'Type'类的理解请参考示例2。

示例 1:在此 deserilizeResturant 中,我们使用 Employee[] 数组并获取详细信息

public static void deserializeResturant(){

       String empList ="[{\"name\":\"Ram\",\"empId\":1},{\"name\":\"Surya\",\"empId\":2},{\"name\":\"Prasants\",\"empId\":3}]";
       Gson gson = new Gson();
       Employee[] emp = gson.fromJson(empList, Employee[].class);
       int numberOfElementInJson = emp.length();
       System.out.println("Total JSON Elements" + numberOfElementInJson);
       for(Employee e: emp){
           System.out.println(e.getName());
           System.out.println(e.getEmpId());
       }
   }

示例 2:

//Above deserilizeResturant used Employee[] array but what if we need to use List<Employee>
public static void deserializeResturantUsingList(){

    String empList ="[{\"name\":\"Ram\",\"empId\":1},{\"name\":\"Surya\",\"empId\":2},{\"name\":\"Prasants\",\"empId\":3}]";
    Gson gson = new Gson();

    // Additionally we need to se the Type then only it accepts List<Employee> which we sent here empTypeList
    Type empTypeList = new TypeToken<ArrayList<Employee>>(){}.getType();


    List<Employee> emp = gson.fromJson(empList, empTypeList);
    int numberOfElementInJson = emp.size();
    System.out.println("Total JSON Elements" + numberOfElementInJson);
    for(Employee e: emp){
        System.out.println(e.getName());
        System.out.println(e.getEmpId());
    }
}

Refer to example 2 for 'Type' class understanding of Gson.

Example 1: In this deserilizeResturant we used Employee[] array and get the details

public static void deserializeResturant(){

       String empList ="[{\"name\":\"Ram\",\"empId\":1},{\"name\":\"Surya\",\"empId\":2},{\"name\":\"Prasants\",\"empId\":3}]";
       Gson gson = new Gson();
       Employee[] emp = gson.fromJson(empList, Employee[].class);
       int numberOfElementInJson = emp.length();
       System.out.println("Total JSON Elements" + numberOfElementInJson);
       for(Employee e: emp){
           System.out.println(e.getName());
           System.out.println(e.getEmpId());
       }
   }

Example 2:

//Above deserilizeResturant used Employee[] array but what if we need to use List<Employee>
public static void deserializeResturantUsingList(){

    String empList ="[{\"name\":\"Ram\",\"empId\":1},{\"name\":\"Surya\",\"empId\":2},{\"name\":\"Prasants\",\"empId\":3}]";
    Gson gson = new Gson();

    // Additionally we need to se the Type then only it accepts List<Employee> which we sent here empTypeList
    Type empTypeList = new TypeToken<ArrayList<Employee>>(){}.getType();


    List<Employee> emp = gson.fromJson(empList, empTypeList);
    int numberOfElementInJson = emp.size();
    System.out.println("Total JSON Elements" + numberOfElementInJson);
    for(Employee e: emp){
        System.out.println(e.getName());
        System.out.println(e.getEmpId());
    }
}
能否归途做我良人 2024-11-06 16:36:39

使用 Kotlin,您可以获得所有自定义可序列化类型的通用 MutableList 类型,

private fun <T : Serializable> getGenericList(
    sharedPreferences: SharedPreferences,
    key: String,
    clazz: KClass<T>
): List<T> {
    return sharedPreferences.let { prefs ->
        val data = prefs.getString(key, null)
        val type: Type = TypeToken.getParameterized(MutableList::class.java, clazz.java).type
        gson.fromJson(data, type) as MutableList<T>
    }
}

您可以调用此函数

getGenericList.(sharedPrefObj, sharedpref_key, GenericClass::class)

using Kotlin, you can get generic MutableList type for all custom Serializable Types

private fun <T : Serializable> getGenericList(
    sharedPreferences: SharedPreferences,
    key: String,
    clazz: KClass<T>
): List<T> {
    return sharedPreferences.let { prefs ->
        val data = prefs.getString(key, null)
        val type: Type = TypeToken.getParameterized(MutableList::class.java, clazz.java).type
        gson.fromJson(data, type) as MutableList<T>
    }
}

you can call this function

getGenericList.(sharedPrefObj, sharedpref_key, GenericClass::class)
昔梦 2024-11-06 16:36:39

就我而言,@uncaught_exceptions 的答案不起作用,我必须使用 List.class 而不是 java.lang.reflect.Type

String jsonDuplicatedItems = request.getSession().getAttribute("jsonDuplicatedItems").toString();
List<Map.Entry<Product, Integer>> entries = gson.fromJson(jsonDuplicatedItems, List.class);

In My case @uncaught_exceptions's answer didn't work, I had to use List.class instead of java.lang.reflect.Type:

String jsonDuplicatedItems = request.getSession().getAttribute("jsonDuplicatedItems").toString();
List<Map.Entry<Product, Integer>> entries = gson.fromJson(jsonDuplicatedItems, List.class);
巴黎夜雨 2024-11-06 16:36:39

我已经为这种情况创建了 GsonUtils lib。我将其添加到 Maven 中央存储库中。

Map<String, SimpleStructure> expected = new HashMap<>();
expected.put("foo", new SimpleStructure("peperoni"));

String json = GsonUtils.writeValue(expected);

Map<String, SimpleStructure> actual = GsonUtils.readMap(json, String.class, SimpleStructure.class);

I have created GsonUtils lib for this case. I add this into maven central repository.

Map<String, SimpleStructure> expected = new HashMap<>();
expected.put("foo", new SimpleStructure("peperoni"));

String json = GsonUtils.writeValue(expected);

Map<String, SimpleStructure> actual = GsonUtils.readMap(json, String.class, SimpleStructure.class);
白色秋天 2024-11-06 16:36:39

Kotlin 和 @Linh 答案的扩展

fun <T> Gson.listFromJson(json: String, clazz: Class<T>): List<T> {
    val typeOfT: Type = TypeToken.getParameterized(MutableList::class.java, clazz).type
    return fromJson(json, typeOfT)
}

版本

val list: List<YourClass> = Gson().listFromJson(it, YouClass::class.java)

Kotlin and extension version of @Linh's answer

fun <T> Gson.listFromJson(json: String, clazz: Class<T>): List<T> {
    val typeOfT: Type = TypeToken.getParameterized(MutableList::class.java, clazz).type
    return fromJson(json, typeOfT)
}

Usage

val list: List<YourClass> = Gson().listFromJson(it, YouClass::class.java)
旧伤慢歌 2024-11-06 16:36:39

我喜欢 kays1 的答案,但我无法实现它。所以我用他的概念构建了自己的版本。

public class JsonListHelper{
    public static final <T> List<T> getList(String json) throws Exception {
        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
        Type typeOfList = new TypeToken<List<T>>(){}.getType();
        return gson.fromJson(json, typeOfList);
    }
}

用法:

List<MyClass> MyList= JsonListHelper.getList(jsonArrayString);

I liked the answer from kays1 but I couldn't implement it. So I built my own version using his concept.

public class JsonListHelper{
    public static final <T> List<T> getList(String json) throws Exception {
        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
        Type typeOfList = new TypeToken<List<T>>(){}.getType();
        return gson.fromJson(json, typeOfList);
    }
}

Usage:

List<MyClass> MyList= JsonListHelper.getList(jsonArrayString);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文