准备工作
我们先看一下本文例子中的 JSON 文件:cars.json
[{"no": "鲁B0001","color": "红色","code": 1},{"no": "鲁B0002","color": "黑色","code": 2},{"no": "鲁B0003","color": "黄色","code": 3},
]
我们需要创建一个 CarDto 类用来处理数组中的每个元素:
package zhangchao.gsonarray;/*** @author zhangchao*/
public class CarDto {private String no;private String color;private Integer code;@Overridepublic String toString() {final StringBuffer sb = new StringBuffer("CarDto{");sb.append("no='").append(no).append('\'');sb.append(", color='").append(color).append('\'');sb.append(", code=").append(code);sb.append('}');return sb.toString();}//// setters/getters/public String getNo() {return no;}public void setNo(String no) {this.no = no;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}
}
读取 JSON 文件的代码:
public static void main(String[] args) {File f = new File("E:\\ws\\zc\\Java8Json\\src\\main\\resources\\JsonFile\\cars.json");StringBuilder sb = new StringBuilder();FileInputStream fis = null;BufferedReader br = null;try {fis = new FileInputStream(f);br = new BufferedReader(new InputStreamReader(fis, "UTF-8"));String str = br.readLine();while(str != null) {sb.append(str);str = br.readLine();}} catch (FileNotFoundException e) {e.printStackTrace();} catch (UnsupportedEncodingException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {if (fis != null) {fis.close();}if (br != null) {br.close();}} catch (IOException e) {e.printStackTrace();}}String json = sb.toString();
// 代码省略
}
检查 JSON 内容的方法:
/*** 检测JSON内容有没有问题。* @param json* @return 没问题返回true,有问题返回false*/
private static boolean check(final String json) {if (json == null) {return false;}String jsonStr = json.trim();if (jsonStr.length() <= 0) {return false;}if (jsonStr.startsWith("[") && jsonStr.endsWith("]")) {return true;}return false;
}
删除列表中 NULL 元素的方法:
/*** 清理掉列表中的 NULL 元素。如果 JSON 数组中最后一个元素末尾带了一个英文逗号* GSON 在处理的时候就会有 NULL 元素* @param list CarDto的列表*/
private static void clearNull(List<CarDto> list) {if (list == null || list.isEmpty()) {return;}for (Iterator<CarDto> iterator = list.iterator(); iterator.hasNext();) {CarDto item = iterator.next();if (item == null) {iterator.remove();}}
}
为什么要写这篇文章?
Gson 没法像处理 JSON 对象一样处理 JSON 数组,比如下面的例子:
Gson gson = new Gson();
List<CarDto> result = gson.fromJson(json, new ArrayList<CarDto>().getClass());
系统会报出如下错误:
Exception in thread "main" java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to zhangchao.gsonarray.CarDtoat zhangchao.gsonarray.TestGsonArray.clearNull(TestGsonArray.java:57)at zhangchao.gsonarray.TestGsonArray.m7(TestGsonArray.java:183)at zhangchao.gsonarray.TestGsonArray.main(TestGsonArray.java:238)
或者直接传入Type也一样:
List<CarDto> result = gson.fromJson(json, new ArrayList<CarDto>().getClass().getGenericSuperclass());
系统报错:
Exception in thread "main" java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to zhangchao.gsonarray.CarDtoat zhangchao.gsonarray.TestGsonArray.clearNull(TestGsonArray.java:57)at zhangchao.gsonarray.TestGsonArray.m8(TestGsonArray.java:193)at zhangchao.gsonarray.TestGsonArray.main(TestGsonArray.java:238)
第一种方法
思路是既然 Gson 处理 JSON 对象比较容易,就把 JSON 数组转换成 JSON 对象。比如把下面的 JSON 数组:
[ element_0, element_1, ... ]
转变成下面的形式:
{"list": [ element_0, element_1, ... ] }
为了达成这个目标,我们先要编写一个 DTO 类来做接收对象,本例子中是 CarRootDto.java
/*** @author zhangchao*/
public class CarRootDto {private List<CarDto> list;public List<CarDto> getList() {return list;}public void setList(List<CarDto> list) {this.list = list;}
}
然后拼接字符串,让 Gson 按照 JSON 对象进行转换:
/*** 第一种方法 GSON 解析数组,给 JSON 外面套了一对大括号和属性,转变成对象* 再让 GSON 解析。* @param json* @return CarDto的列表*/public static List<CarDto> m1 (final String json) {if (!check(json)) {return null;}String jsonStr = json.trim();jsonStr = "{\"list\": " + jsonStr + "}";Gson gson = new Gson();CarRootDto root = gson.fromJson(jsonStr, CarRootDto.class);clearNull(root.getList());return root.getList();}
第二种方法
因为 Java8 有泛型擦除的特性,所以下面的代码:
Type type = new ArrayList<CarDto>().getClass().getGenericSuperclass();
System.out.println(type);
运行结果是:
java.util.AbstractList<E>
也就是说 Class 类的 getGenericSuperclass
方法根本拿不到父类泛型实际传入的类型。
如果要解决这个问题,我们需要编写一个类,去继承 ArrayList<CarDto>
,注意这里父类是有明确指定的类。
通常我们为了编写代码方便,会加个静态内部类,代码如下:
/*** 测试 GSON 读取 JSON 数组* @author zhangchao*/
public class TestGsonArray {
/*** 配合第二种方法的,继承 ArrayList<CarDto> 的静态内部类。*/private static class TmpList extends ArrayList<CarDto> {}/*** 第二种方法,编写一个继承 ArrayList<CarDto> 的类,然后调用 Class 类的* getGenericSuperclass() 方法获取父类的Type。* 这么干的原因是 Java8 有泛型擦除。** @param json* @return CarDto的列表*/public static List<CarDto> m2 (final String json) {if (!check(json)) {return null;}Type type = TmpList.class.getGenericSuperclass();Gson gson = new Gson();List<CarDto> result = gson.fromJson(json, type);clearNull(result);return result;}// 其他代码省略 .....
}
第三种方法
此方法原理和第二种方法相同,只是为了代码简洁把静态内部类改成了匿名内部类。代码如下:
public static List<CarDto> m3 (final String json) {if (!check(json)) {return null;}Gson gson = new Gson();Type type = new ArrayList<CarDto>(){}.getClass().getGenericSuperclass();List<CarDto> result = gson.fromJson(json, type);clearNull(result);return result;
}
第四种方法
编写一个新类 ZcTypeTool<T>
,创建匿名内部类对象的时候,泛型传入想要获得的类型。然后在构造方法中,匿名内部类的对象获取父类 Type,并强制转换成 ParameterizedType,进而获取泛型 T 的 Type。
ZcTypeTool.java 文件的代码:
package zhangchao.gsonarray;import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;/*** 获取泛型 T 的Type* @param <T> 泛型* @author zhangchao*/
public class ZcTypeTool<T> {// 泛型 T 的 Typeprivate Type type;public ZcTypeTool() {Type superType = this.getClass().getGenericSuperclass();ParameterizedType p = ((ParameterizedType) superType);Type types[] = p.getActualTypeArguments();this.type = types[0];}public Type getType() {return type;}}
调用 Gson 的代码:
public static List<CarDto> m4 (final String json) {if (!check(json)) {return null;}Gson gson = new Gson();Type type = new ZcTypeTool<List<CarDto>>(){}.getType();List<CarDto> result = gson.fromJson(json, type);clearNull(result);return result;
}
第五种方法
原理等同于第四种,但使用的是 GSON 库提供的 TypeToken 类。代码如下:
public static List<CarDto> m5 (final String json) {if (!check(json)) {return null;}Gson gson = new Gson();Type type = new TypeToken<List<CarDto>>(){}.getType();List<CarDto> result = gson.fromJson(json, type);clearNull(result);return result;
}
第六种方法
原理同第四种,只是取消了一个自定义类的封装,转而使用任意一个有泛型的类。下面代码中的 Stack<T>
类可以换成任何一个接受泛型的类。
public static List<CarDto> m6 (final String json) {if (!check(json)) {return null;}Gson gson = new Gson();Type superType = new Stack<List<CarDto>>(){}.getClass().getGenericSuperclass();ParameterizedType p = (ParameterizedType) superType;Type[] types = p.getActualTypeArguments();Type type = types[0];List<CarDto> result = gson.fromJson(json, type);clearNull(result);return result;
}
全部演示代码
代码如下:
package zhangchao.gsonarray;import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.Stack;/*** 测试 GSON 读取 JSON 数组* @author zhangchao*/
public class TestGsonArray {/*** 检测JSON内容有没有问题。* @param json* @return 没问题返回true,有问题返回false*/private static boolean check(final String json) {if (json == null) {return false;}String jsonStr = json.trim();if (jsonStr.length() <= 0) {return false;}if (jsonStr.startsWith("[") && jsonStr.endsWith("]")) {return true;}return false;}/*** 清理掉列表中的 NULL 元素。如果 JSON 数组中最后一个元素末尾带了一个英文逗号* GSON 在处理的时候就会有 NULL 元素* @param list CarDto的列表*/private static void clearNull(List<CarDto> list) {if (list == null || list.isEmpty()) {return;}for (Iterator<CarDto> iterator = list.iterator(); iterator.hasNext();) {CarDto item = iterator.next();if (item == null) {iterator.remove();}}}/*** 第一种方法 GSON 解析数组,给 JSON 外面套了一对大括号和属性,转变成对象* 再让 GSON 解析。* @param json* @return CarDto的列表*/public static List<CarDto> m1 (final String json) {if (!check(json)) {return null;}String jsonStr = json.trim();jsonStr = "{\"list\": " + jsonStr + "}";Gson gson = new Gson();CarRootDto root = gson.fromJson(jsonStr, CarRootDto.class);clearNull(root.getList());return root.getList();}/*** 配合第二种方法,继承 ArrayList<CarDto> 的静态内部类。*/private static class TmpList extends ArrayList<CarDto> {}/*** 第二种方法,编写一个继承 ArrayList<CarDto> 的类,然后调用 Class 类的* getGenericSuperclass() 方法获取父类的Type。* 这么干的原因是 Java8 有泛型擦除。** @param json* @return CarDto的列表*/public static List<CarDto> m2 (final String json) {if (!check(json)) {return null;}Type type = TmpList.class.getGenericSuperclass();Gson gson = new Gson();List<CarDto> result = gson.fromJson(json, type);clearNull(result);return result;}/*** 第三种方法,使用匿名内部类,获取父类的Type。原理和第二种相同,* 但是使用匿名内部类使得代码更简洁。* @param json* @return CarDto的列表*/public static List<CarDto> m3 (final String json) {if (!check(json)) {return null;}Gson gson = new Gson();Type type = new ArrayList<CarDto>(){}.getClass().getGenericSuperclass();List<CarDto> result = gson.fromJson(json, type);clearNull(result);return result;}/*** 第四种方法,编写一个新类,用泛型接受要获得的类型,然后用匿名内部类获取父类* 的 Type 并强制转换成 ParameterizedType 来获取泛型 T 的 Type* @param json* @return CarDto的列表*/public static List<CarDto> m4 (final String json) {if (!check(json)) {return null;}Gson gson = new Gson();Type type = new ZcTypeTool<List<CarDto>>(){}.getType();List<CarDto> result = gson.fromJson(json, type);clearNull(result);return result;}/*** 第五种方法,使用 GSON 库自带的 TypeToken,其实现原理等同于第四种。* @param json* @return CarDto的列表*/public static List<CarDto> m5 (final String json) {if (!check(json)) {return null;}Gson gson = new Gson();Type type = new TypeToken<List<CarDto>>(){}.getType();List<CarDto> result = gson.fromJson(json, type);clearNull(result);return result;}/*** 第六种方法,原理同第四种,只是取消了一个自定义类的封装,转而使用任意一个有泛型的类。* @param json* @return CarDto的列表*/public static List<CarDto> m6 (final String json) {if (!check(json)) {return null;}Gson gson = new Gson();Type superType = new Stack<List<CarDto>>(){}.getClass().getGenericSuperclass();ParameterizedType p = (ParameterizedType) superType;Type[] types = p.getActualTypeArguments();Type type = types[0];List<CarDto> result = gson.fromJson(json, type);clearNull(result);return result;}public static List<CarDto> m7 (final String json) {if (!check(json)) {return null;}Gson gson = new Gson();List<CarDto> result = gson.fromJson(json, new ArrayList<CarDto>().getClass());clearNull(result);return result;}public static List<CarDto> m8 (final String json) {if (!check(json)) {return null;}Gson gson = new Gson();List<CarDto> result = gson.fromJson(json, new ArrayList<CarDto>().getClass().getGenericSuperclass());clearNull(result);return result;}public static void main(String[] args) {File f = new File("E:\\ws\\zc\\Java8Json\\src\\main\\resources\\JsonFile\\cars.json");StringBuilder sb = new StringBuilder();FileInputStream fis = null;BufferedReader br = null;try {fis = new FileInputStream(f);br = new BufferedReader(new InputStreamReader(fis, "UTF-8"));String str = br.readLine();while(str != null) {sb.append(str);str = br.readLine();}} catch (FileNotFoundException e) {e.printStackTrace();} catch (UnsupportedEncodingException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {if (fis != null) {fis.close();}if (br != null) {br.close();}} catch (IOException e) {e.printStackTrace();}}String json = sb.toString();System.out.println(m1(json));System.out.println(m2(json));System.out.println(m3(json));System.out.println(m4(json));System.out.println(m5(json));System.out.println(m6(json));
// System.out.println(m8(json));}
}