我想通过 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 JsonParseExceptions
和 result 不为空。
我使用调试器检查了 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.
发布评论
评论(16)
反序列化泛型集合的方法:
由于评论中已经有好几个人提到了,这里解释一下
TypeToken
类的使用方法。构造new TypeToken<...>() {}.getType()
捕获编译时类型(在<
和>< 之间) /code>) 转换为运行时
java.lang.reflect.Type
对象。与只能表示原始(已删除)类型的Class
对象不同,Type
对象可以表示 Java 语言中的任何类型,包括泛型类型的参数化实例化。TypeToken
类本身没有公共构造函数,因为您不应该直接构造它。相反,您始终构造一个匿名子类(因此{}
,它是该表达式的必要部分)。由于类型擦除,
TypeToken
类只能捕获在编译时完全已知的类型。 (也就是说,您不能对类型参数T
执行new TypeToken
。)>() {}.getType()
了解更多信息,请参阅 TypeToken 的文档 类。
Method to deserialize generic collection:
Since several people in the comments have mentioned it, here's an explanation of how the
TypeToken
class is being used. The constructionnew TypeToken<...>() {}.getType()
captures a compile-time type (between the<
and>
) into a runtimejava.lang.reflect.Type
object. Unlike aClass
object, which can only represent a raw (erased) type, theType
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 donew TypeToken<List<T>>() {}.getType()
for a type parameterT
.)For more information, see the documentation for the
TypeToken
class.另一种方法是使用数组作为类型,例如:
这样您就可以避免 Type 对象的所有麻烦,如果您确实需要一个列表,您可以随时将数组转换为列表:
恕我直言,这更具可读性。
要使其成为一个实际列表(可以修改,请参阅 Arrays.asList() 的限制),然后只需执行以下操作:
Another way is to use an array as a type, e.g.:
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:
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:从 Gson 2.8 开始,我们可以像这样创建 util 函数:
示例用法:
Since Gson 2.8, we can create util function like this:
Example usage:
参考这篇文章。
Java 类型泛型作为 GSON 的参数
我对此有更好的解决方案。这是列表的包装类,因此包装器可以存储列表的确切类型。
然后,代码可以很简单:
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.
And then, the code can be simple:
好吧,另一种方法可以达到相同的结果。我们使用它是因为它的可读性。
不要执行这个难以阅读的句子:
创建一个扩展对象列表的空类:
并在解析 JSON 时使用它:
Wep, another way to achieve the same result. We use it for its readability.
Instead of doing this hard-to-read sentence:
Create a empty class that extends a List of your object:
And use it when parsing the JSON:
对于 Kotlin 来说,简单来说:
或者,这里有一个有用的函数:
然后,使用:
For Kotlin simply:
or, here is a useful function:
Then, to use:
例子:
Example:
由于它回答了我原来的问题,我已经接受了 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.
这是一个适用于动态定义类型的解决方案。诀窍是使用 Array.newInstance() 创建正确类型的数组。
Here is a solution that works with a dynamically defined type. The trick is creating the proper type of of array using
Array.newInstance()
.我想补充一种可能性。如果你不想使用 TypeToken 并且想要将 json 对象数组转换为 ArrayList,那么你可以像这样继续:
如果你的 json 结构是这样的:
}
并且你的类结构是这样的:
那么你可以像这样解析它:
现在您可以访问 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:
}
and your class structure is like:
then you can parse it like:
Now you can access each element of className object.
Gson 'Type'类的理解请参考示例2。
示例 1:在此 deserilizeResturant 中,我们使用 Employee[] 数组并获取详细信息
示例 2:
Refer to example 2 for 'Type' class understanding of Gson.
Example 1: In this deserilizeResturant we used Employee[] array and get the details
Example 2:
使用 Kotlin,您可以获得所有自定义可序列化类型的通用 MutableList 类型,
您可以调用此函数
using Kotlin, you can get generic MutableList type for all custom Serializable Types
you can call this function
就我而言,@uncaught_exceptions 的答案不起作用,我必须使用
List.class
而不是java.lang.reflect.Type
:In My case @uncaught_exceptions's answer didn't work, I had to use
List.class
instead ofjava.lang.reflect.Type
:我已经为这种情况创建了 GsonUtils lib。我将其添加到 Maven 中央存储库中。
I have created GsonUtils lib for this case. I add this into maven central repository.
Kotlin 和 @Linh 答案的扩展
版本
Kotlin and extension version of @Linh's answer
Usage
我喜欢 kays1 的答案,但我无法实现它。所以我用他的概念构建了自己的版本。
用法:
I liked the answer from kays1 but I couldn't implement it. So I built my own version using his concept.
Usage: