【200】Java8 调用 Gson2.10 读取 JSON 数组的六种方法。

准备工作

我们先看一下本文例子中的 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));}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/794577.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

视频分块上传Vue3+SpringBoot3+Minio

文章目录 一、简化演示分块上传、合并分块断点续传秒传 二、更详细的逻辑和细节问题可能存在的隐患 三、代码示例前端代码后端代码 一、简化演示 分块上传、合并分块 前端将完整的视频文件分割成多份文件块&#xff0c;依次上传到后端&#xff0c;后端将其保存到文件系统。前…

C++读取.bin二进制文件

C读取.bin二进制文件 在C中&#xff0c;可以使用文件输入/输出流来进行二进制文件的读写操作&#xff0c;方便数据的保存和读写。 //C读取bin二进制文件 int read_bin() {std::ifstream file("data_100.bin", std::ios::in | std::ios::binary);if (file) {// 按照…

【拓扑空间】示例及详解1

例1 度量空间的任意两球形邻域的交集是若干球形邻域的并集 Proof&#xff1a; 任取空间的两个球形邻域、&#xff0c;令 任取,令 球形领域 例2 规定X的子集族,证明是X上的一个拓扑 Proof&#xff1a; 1. 2., &#xff08;若干个球形邻域的并集都是的元素&#xff0c;元素…

SSM 项目学习(Vue3+ElementPlus+Axios+SSM)

文章目录 1 项目介绍1.1 项目功能/界面 2 项目基础环境搭建2.1 创建项目2.2 项目全局配置 web.xml2.3 SpringMVC 配置2.4 配置 Spring 和 MyBatis , 并完成整合2.5 创建表&#xff0c;使用逆向工程生成 Bean、XxxMapper 和 XxxMapper.xml2.6 注意事项和细节说明 3 实现功能 01-…

Java 开发者必备:JDK 版本详解与选择策略(含安装与验证)

1. JDK 版本 (Oracle Java SE 支持路线图) 数据来源&#xff1a;Oracle Java SE 支持路线图 | 甲骨文中国: https://www.oracle.com/cn/java/technologies/java-se-support-roadmap.html 版本GA DatePremier Support UntilExtended Support Until&#xff08;限 LTS&#xff09…

虚幻UE5数字孪生蓝图开发教程

一、背景 这几年&#xff0c;智慧城市/智慧交通/智慧水利等飞速发展&#xff0c;骑士特意为大家做了一个这块的学习路线。 二、这是学习大纲 1.给虚幻UE5初学者准备的智慧城市/数字孪生蓝图开发教程 https://www.bilibili.com/video/BV1894y1u78G 2.UE5数字孪生蓝图开发教学…

亚马逊跨境电商平台真人测评和自养号测评有什么区别?

下面来讲一下真人测评和自养号测评的优缺点有哪些 真人测评 优点&#xff1a;权重高&#xff0c;可以有效提升转化率 缺点&#xff1a;市面上的渠道良莠不齐&#xff0c;质量难以保证&#xff0c;且较难选择 真人测评是通过真人的买家在页面留下review的方式来提高权重&…

关于 QSound播放wav音频文件,播放失败“using null output device, none available” 的解决方法

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/137264493 红胖子(红模仿)的博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软…

面试算法-142-找到字符串中所有字母异位词

题目 给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#xff08;包括相同的字符串&#xff09;。 示例 1: 输入: s “cbaebabacd”, p “abc”…

MySQL介绍和安装

MySQL介绍和安装 文章目录 MySQL介绍和安装1.MySQL介绍2.MySQL安装2.1 主机初始化2.1.1 设置网卡名和ip地址2.1.2 配置镜像源2.1.3 关闭防火墙2.1.4 禁用SELinux2.1.5 设置时区 2.2 包安装2.2.1 Rocky和CentOS 安装 MySQL2.2.2 Ubuntu 安装 MySQL 2.3 二进制安装安装MySQL2.3.1…

【随笔】Git 高级篇 -- 整理提交记录(上)(十五)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

地质地貌卫星影像集锦(三 矿产资源篇)

1. 元古代沉积岩的抬升 这个地区位于Leigh Creek中部&#xff0c;距离澳大利亚南部的阿德莱德约500km&#xff0c;弗林德斯山脉的北面是Gawler克拉通。弗林德斯山脉是由元古代沉积岩抬升后形成的块体&#xff0c;在其之下的是寒武纪的岩石&#xff0c;它座落在距阿德莱德北…

Docker 容器编排技术解析与实践

探索了容器编排技术的核心概念、工具和高级应用&#xff0c;包括 Docker Compose、Kubernetes 等主要平台及其高级功能如网络和存储管理、监控、安全等。此外&#xff0c;文章还探讨了这些技术在实际应用中的案例&#xff0c;提供了对未来趋势的洞见。 一、容器编排介绍 容器编…

蓝桥杯(5):python动态规划DF[2:背包问题]

1 0-1背包介绍【每件物品只能拿1件或者不拿】 1.1 简介 贪心是不可以的&#xff01;&#xff01;&#xff01; 1.2 状态 及状态转移 转移解释&#xff1a;要么不选 则上一个直接转移过来【dp[i-1][j]】&#xff0c;要么是选这个之后体积为j 则上一个对应的就是【dp[i-1][j-wi]…

数据结构day2--双向链表

双向链表: 即可以从头遍历到尾部和从尾部遍历到头部的链表&#xff0c;每个结点包括两个链域&#xff1a;前驱指针域和后继指针域&#xff0c;所以比起单向链表&#xff0c;其可以在任意一个结点访问前后两个结点 关于双向链表的一个完整步骤为&#xff1a; 创建一个表头结构…

蓝桥杯第十三届--选数异或

题目描述 给定一个长度为 n 的数列 A1, A2, , An 和一个非负整数 x&#xff0c;给定 m 次查询, 每次询问能否从某个区间 [l,r] 中选择两个数使得他们的异或等于 x 。 输入格式 输入的第一行包含三个整数 n, m, x 。 第二行包含 n 个整数 A1, A2, , An 。 接下来 m 行…

MySQL 全景图

前言 MySQL 是啥&#xff1f;我一直以为 MySQL 是数据库&#xff0c;直到我最近看了很多关于 MySQL 的文章、专栏以及书籍后&#xff0c;我对 MySQL 的才有了更加深刻的体会。 原来 MySQL 并不是数据库&#xff0c;又或者说&#xff0c;我认为“ MySQL 是数据库这种想法”是片…

前端被问到项目亮点和项目难点

在前端面试中&#xff0c;被问到项目亮点是很常见的。项目亮点是指在你的前端项目中&#xff0c;具有特殊或者突出的功能、技术或者设计等方面的亮点。以下是一些常见的前端项目亮点&#xff1a; 响应式设计&#xff1a;如果你的项目能够适应不同屏幕尺寸和设备&#xff0c;提供…

Vue3:Pinia中的getters

一、情景说明 Pinia中的getters的作用类似于Vue中的计算属性computed 二、案例 1、配置getters count.ts import {defineStore} from piniaexport const useCountStore defineStore(count,{// actions里面放置的是一个一个的方法&#xff0c;用于响应组件中的“动作”acti…

该主机与 Cloudera Manager Server 失去联系的时间过长。 该主机未与 Host Monitor 建立联系

该主机与 Cloudera Manager Server 失去联系的时间过长。 该主机未与 Host Monitor 建立联系 这个去集群主机cm界面上看会出现这个错误 排查思路&#xff1a; 一般比较常见的原因可能是出问题的主机和集群主节点的时间对应不上了。还有就是cm agent服务出现问题了 去该主机的…