Java泛型(⼆)——使⽤Gson解析复杂的泛型嵌套泛型数据结构
⼀、Gson
Gson是由Google⾃家出来的Json解析库,使⽤起来兼容性⽅⾯当然会有很多优势,⽽且解析Json数据也很⽅便,掌握了Gson的⼀些基本使⽤之后就可以使⽤它解析很多复杂的Json数据了。但当我们要解析⼀个复杂的数据结构时,⽐如说List<CardBean<E>>这种,泛型之中还有泛型的数组结构,就⽐较⿇烦了。下⾯我会给出⼀种⽅案,在此之前我还是先简单的介绍⼀下Gson的⼀些东西,如果懂的⼤神请直接跳过,直接看第⼆节。
在使⽤Gson之前,你需要了解⼀下 反射 相关的知识和 泛型 相关的知识。
泛型的相关知识请参考我的第⼀篇:
Gson中的⼏个重要对象
JsonParser : 解析器,可以将String类型的Json数据解析成JsonElement元素。
JsonElement: Json的基本对象元素,它的拓展类有:JsonObject、JsonArray、JsonPrimitive。不同的类封装了不同的⽅法,以便于更好的调⽤。
JsonObject: Json的对象,类似于Sun⾥⾯的JSONObject,封装了对象的信息。
JsonArray: Json的数组,同样类似于Sun⾥⾯的JSONArray,封装了对象数组信息(或者⼦数组信息).
JsonPrimitive: Json原始对象,Sun提供的JSON中没有这个类,这⾥它封装了Java中基本对象。
Gson: Gson对象,⽤来将程序中定义的对象转换成String类型的Json格式;同时也可以将String类型的Json格式转换成我们想要的对象。
TypeToken: ⽤于提取泛型参数的类型,⽤于在解析器将Json解析成我们的实际对象时,在提供给反射机制,以便于实例化具体的类别。
Gson解析泛型
使⽤Gson的时候,我们⼀般⽆外乎是将Json格式的字符串解析成对象、以及将对象转化成Json格式以便于传输。
它的最基本的两个⽅法如下:
toJson(obj,type): 将对象转化为Json字符串
fromJson(obj,type): 将Json字符串转为type对应的对象
上⾯的使⽤⽅法⼤家都知道,我就不举例了。
但当我们使⽤Gson去解析泛型的时候,我⽬前就遇到两个问题:
问题:
解析出来的泛型对象是⼀个LinkedTreeMap,这个问题⽐较普遍,解决⽅法也⽐较简单。是因为使⽤fromJson的时候没有给泛型类别添加Type说明,实例下⾯代码附上。
/**
* 第⼀种,没有在将类型的泛型Type传⼊,则得到的泛型对象是⼀个LinkedTreeMap
*/
public static void test1() {
CardBean<CardBean1000> cardBean = new CardBean<>();
cardBean.cardViewType = 1000;
CardBean1000 cardBean1000 = new CardBean1000();
cardBean1000.agenda = "男";
cardBean1000.name = "张三";
cardBean.data = cardBean1000;
String jsonStr = Json(cardBean);
System.out.println(jsonStr);
CardBean transCard = gson.fromJson(jsonStr, Class());
System.out.println(Class().getName());
}
/**
* 第⼆种,将含有泛型具体的类别Type信息传⼊,则将能够转为具体的类别。
*/
public static void test2() {
CardBean<CardBean1000> cardBean = new CardBean<>();
cardBean.cardViewType = 1000;
CardBean1000 cardBean1000 = new CardBean1000();
cardBean1000.agenda = "男";
cardBean1000.name = "张三";
cardBean.data = cardBean1000;
Type type = new TypeToken<CardBean<CardBean1000>>() {
}.getType();
String jsonStr = Json(cardBean);
System.out.println(jsonStr);
CardBean transCard = gson.fromJson(jsonStr, type);
System.out.println(Class().getName());
;
}
程序结果对⽐:
test1——将对象转化成Json格式:  {"data":{"name":"张三","agenda":"男"},"cardViewType":1000}
test1——没有传⼊泛型类别:le.gson.internal.LinkedTreeMap
test2——传⼊泛型类别:  main.Test$CardBean1000
将对象转化成Json格式并没有什么⼤问题,在test1中没有传⼊泛型的Type类别,因此解析器解析的时候没有办法确定泛型的类型,框架中默认将KV形式的Json保存在了LinkedTreeMap中,将其赋值给了泛型 T,我们只要使⽤TypeToken将泛型信息给提取出来,那么解析器解析的时候就可以具体知道类
别(Class),便可以⽤反射来实例化对象了。
⼆、Gson解析嵌套泛型数组
当我们服务器返回的Json数组中的对象不同时,就会出现解析List<CardBean<E>>这样的嵌套泛型数组问题。
解析⽅案
解析上述类型的时候,我们直接使⽤fromJson(json,Type),是⽆法⼀步到位的,我们只能拿到⼀个List<CardBean> 的数组列表,但CardBean<T> 的泛型我们并没有解析,实际上也⽆法解析,因为⾥⾯是什么样的对象我们是不知道的,我们也⽆法确定数组中的某⼀个位置是什么样的类型。同样类似于上⾯的问题,Gson会把CardBean 中的 T 解析成⼀个LinkedTreeMap,第⼀次解析我们只能拿到这个信息,我们如何知道⾥⾯的json对象是对应的那⼀个类呢?我们使⽤数据结构的⽅法解决这个问题,这⾥我们可以在CardBean对象⾥增加⼀个type字段,该type与具体的类有⼀个映射关系(可以存储在HashMap中,也可以使⽤枚举)。
说到这⾥,很多⼈应该知道我的思路了,就是我⾃⼰做⼀次解析,取得CardBean⾥⾯的type字段,再通过映射关系到对应的class,然后通过反射解析该对象。通过这样的⽅式我们就可以达到解析复杂嵌套泛型的⽬的了,这⾥是两层,多层的话可以使⽤递归⽅式,或者循环的⽅式解决 。
说了那么多,show me the code! 附上代码吧:
public static void cardViewTypeParse() {
CardBean<CardBean1000> cardBean = new CardBean<>();
CardBean1000 cardBean1000 = new CardBean1000();
cardBean1000.agenda = "⼥";
cardBean1000.name = "何⼩兰";
cardBean.data = cardBean1000;
cardBean.cardViewType = 1000;
CardBean<CardBean1001> cardBean1 = new CardBean<>();
CardBean1001 cardBean1001 = new CardBean1001();
cardBean1001.agenda = "男";json值的类型有哪些
cardBean1001.name = "景晓明";
Extra extra = new Extra();
cardBean1.data = cardBean1001;
cardBean1.cardViewType = 1001;
JsonArray jArray = new JsonArray();
JsonObject jObject = new JsonObject();
// TODO toJson(Object src,Type typeOfSrc)具体⽤来⼲嘛?有没有感觉都没什么区别,⼤神指导⼀下        jArray.add(parser.Json(cardBean, Class())));
jArray.add(parser.Json(cardBean1, Class())));
System.out.String());
List<CardBean> list0 = gson.String(), new TypeToken<List<CardBean>>() {
}.getType());
for (CardBean cardBean2 : list0) {
System.out.println(cardBean2.cardViewType);
CardViewType cardViewType = CardViewType(cardBean2.cardViewType);
CardBeanClient cardBeanClient = new CardBeanClient<>();
cardBeanClient.cardViewType = cardViewType;
cardBeanClient.data = gson.Json(cardBean2.data), cardViewType.clazz);
System.out.println(String());
if (cardBean2.data instanceof Map) {
System.out.println(((Map) cardBean2.data).get("name"));
}
}
}
/**
* Client端的Bean包装类
* @author David
*
* @param <T>
*/
public static class CardBeanClient<T> {
T data;
CardViewType cardViewType;
}
/**
* Server端的Bean包装类
* @author David
*
* @param <T>
*/
public static class CardBeanServer<T> {
T data;
int cardViewType;
int cardViewType;
@Override
public String toString() {
// TODO Auto-generated method stub
String() + "    " + cardViewType;
}
}
/**
* 枚举:type与class的映射关系
*/
public enum CardViewType {
CardViewType1000(1000, CardBean1000.class), CardViewType1001(1001, CardBean1001.class);
int type;
Class clazz;
CardViewType(int type, Class clazz) {
this.clazz = clazz;
}
// 通过value获取对应的枚举对象
public static CardViewType getCardViewType(int value) {
for (CardViewType cardViewType : CardViewType.values()) {
if (value == pe) {
return cardViewType;
}
}
return null;
}
}
/**
* 具体的Bean数据
*/
public static class CardBean1000 {
String name;
String agenda;
@Override
public String toString() {
// TODO Auto-generated method stub
return name + "    " + agenda;
}
}
public static class CardBean1001 {
String name;
String agenda;
Extra extra;
@Override
public String toString() {
// TODO Auto-generated method stub
return name + "    " + agenda + "    " + a;
}
}
public static class Extra {
String extra;
}
结果打印:
json:[{"data":{"name":"何⼩兰","agenda":"⼥"},"cardViewType":1000},{"data":{"name":"景晓明","agenda":"男","extra":{"extra":"extra"}},"cardViewType"
1000
type = CardViewType1000  main.Test$CardBean1000
1001
type = CardViewType1001  main.Test$CardBean1001
同时也希望各位⼤神能够给出⼀个动态解析嵌套泛型数组的稳定、可靠、⾼效的框架,⼩弟不胜感激!最近做⼀个项⽬中需要⽤到这些知识,希望⼤神给⼀些建议。
疑惑点(希望⼤神能够帮助解决⼀下):
toJson(Object src,Type typeOfSrc) 具体⽤来⼲嘛?使⽤toJson(Object str)同样会调⽤它,那么这⾥如果我直接使⽤toJson(Object src,Type typeOfSrc)能够达到什么其他的效果吗?
没有仔细研究源码,从使⽤的⾓度上来看觉得封装仅仅是为了⽅便。因为我解析的时候调⽤的是fromJson(String jsonStr,Type typeOfSrc);这⾥已经指定了type,那么上⾯的toJson(Object src,Type typeOfSrc)⼜是⼲嘛的?系统库要做这⼀层封装⼲嘛?有什么其他的⽤途吗?

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。