Gson 解析 Json 容错才是关键,举几个常用的实例!,2个月面试腾讯、B站、网易等11家公司的面经总结

val newGson = Gson()
return newGson.fromJson(json, typeOfT)
}else{
return Collections.EMPTY_LIST
}
}
}

fun listDefaultEmpty(){
val jsonStr = “”"
{
“name”:“承香墨影”,
“age”:“18”,
“languages”:{}
}
“”".trimIndent()
val user = GsonBuilder()
.registerTypeHierarchyAdapter(
List::class.java,
ArraySecurityAdapter())
.create()
.fromJson(jsonStr,User::class.java)
Log.i(“cxmydev”,“user: ${user.toString()}”)
}

其核心就是 isJsonArray() 方法,判断当前是否是一个 JSONArray,如果是,再具体解析即可。到这一步就很灵活了,你可以直接用 Gson 将数据反序列化成一个 List,也可以将通过一个 for 循环将其中的每一项单独反序列化。

需要注意的是,如果依然想用 Gson 来解析,需要重新创建一个新的 Gson 对象,不可以直接复用 JsonDeserializationContext,否则会造成递归调用。

另外还有一个细节,在这个例子中,调用的是 registerTypeHierarchyAdapter() 方法来注册 TypeAdapter,它和我们前面介绍的 registerTypeAdapter() 有什么区别呢?

通常我们会根据不同的场景,选择不同数据结构实现的集合类,例如 ArrayList 或者 LinkedList。但是 registerTypeAdapter() 方法,要求我们传递一个明确的类型,也就是说它不支持继承,而 registerTypeHierarchyAdapter() 则可以支持继承。

我们想用 List 来替代所有的 List 子类,就需要使用 registerTypeHierarchyAdapter() 方法,或者我们的 Java Bean 中,只使用 List。这两种情况都是可以的。

2.4 保留原 Json 字符串

看到这个小标题,可能会有疑问,保留原 Json 字符串是一个什么情况?得到的 Json 数据,本身就是一个字符串,且挺我细细说来。

举个例子,前面定义的 User 类,需要存到 SQLite 数据库中,语言(languages)字段也是需要存储的。说到 SQLite,当然优先使用一些开源的 ORM 框架了,而不少优秀的 ORM-SQLite 框架,都通过外键的形式支持了一对多的存储。例如一篇文章对应多条评论,一条用户信息对应对应多条语言信息。

这种场景下我们当然可以使用 ORM 框架本身提供的一对多的存储形式。但是如果像现在的例子中,只是简单的存储一些有限的数据,例如用户会的语言(languages),这种简单的有限数据,用外键有一些偏重了。

此时我们就想,要是可以直接在 SQLite 中存储 languages 字段的 JSON,将其当成一个字符串去存储,是不是就简单了?把一个多级的结构拉平成一级,剩下的只需要扩展出一个反序列化的方法,对业务来说,这些操作都是透明的。

那拍脑袋想,如果 Gson 有简单的容错,那我们将这个解析的字段类型定义成 String,是不是就可以做到了?

@SerializedName(“languages”)
var languageStr = “”

很遗憾,这并没有办法做到,如果你这样使用,你将得到一个 IllegalStateException 的异常。

Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_ARRAY at line 4 column 18 path $.languages

之所以会出现这样的情况,简单来说,虽然 deserialize() 方法传递的参数都是 JsonElement,但是 JsonElement 只是一个抽象类,最终会根据数据的情况,转换成它的几个实现类的其中之一,这些实现类都是 final class,分别是 JsonObject、JsonArray、JsonPrimitive、JsonNull,这些从命名上就很好理解了,它们代表了不通的 JSON 数据场景,就不过多介绍了。

使用了 Gson 之后,遇到花括号 {} 会生成一个 JsonObject,而字符串则是基本类型的 JsonPrimitive 对象,它们在 Gson 内部的解析流程是不一样的,这就造成了 IllegalStateException 异常。

那么接下来看看如何解决这个问题。

既然 TypeAdapter 是 Gson 解析的银弹,找不到解决方案,用它就对了。思路继续是用 JsonDeserializer 来接管解析,这一次将 User 类的整个解析都接管了。

class UserGsonAdapter:JsonDeserializer{
override fun deserialize(json: JsonElement,
typeOfT: Type?,
context: JsonDeserializationContext?): User {

var user = User()
if(json.isJsonObject){
val jsonObject = JSONObject(json.asJsonObject.toString())
user.name = jsonObject.optString(“name”)
user.age = jsonObject.optInt(“age”)
user.languageStr = jsonObject.optString(“languages”)
user.languages = ArrayList()
val languageJsonArray = JSONArray(user.languageStr)
for(i in 0 until languageJsonArray.length()){
user.languages.add(languageJsonArray.optString(i))
}
}
return user
}
}

fun userGsonStr(){
val jsonStr = “”"
{
“name”:“承香墨影”,
“age”:“18”,
“languages”:[“CN”,“EN”]
}
“”".trimIndent()
val user = GsonBuilder()
.registerTypeAdapter(
User::class.java,
UserGsonAdapter())
.create()
.fromJson(jsonStr,User::class.java)
Log.i(“cxmydev”,“user: \n${user.toString()}”)
}

在这里我直接使用标准 API org.json 包中的类去解析 JSON 数据,当然你也可以通过 Gson 本身提供的一些方法去解析,这里只是提供一个思路而已。

最终 Log 输出的效果如下:

{
“name”:“承香墨影”,
“age”:18,
“languagesJson”:[“CN”,“EN”],
"languages size:"2
}

在这个例子中,最终解析还是使用了标准的 JSONObject 和 JSONArray 类,和 Gson 没有任何关系,Gson 只是起到了一个桥接的作用,好像这个例子也没什么实际用处。

不谈场景说应用都是耍流氓,那么如果是使用 Retrofit 呢?Retrofit 可以配置 Gson 做为数据的转换器,在其内部就完成了反序列化的过程。这种情况,配合 Gson 的 TypeAdapter,就不需要我们在额外的编写解析的代码了,网络请求走一套逻辑即可。

如果觉得在构造 Retrofit 的时候,为 Gson 添加 TypeAdapter 有些入侵严重了,可以配合 @JsonAdapter 注解使用。

三. 小结时刻

针对服务端返回数据的容错处理,很大一部分其实都是来自双端没有保证数据一致的问题。而针对开发者来说,要做到外部数据均不可信的,客户端不信本地读取的数据、不信服务端返回的数据,服务端也不能相信客户端传递的数据。这就是所谓防御式编程。
言归正传,我们小结一下本文的内容:

  1. TypeAdapter(包含JsonSerializer、JsonDeserializer) 是 Gson 解析的银弹,所有 Json 解析的定制化要求都可以通过它来实现。
  2. registerTypeAdapter() 方法需要制定确定的数据类型,如果想支持继承,需要使用 registerTypeHierarchyAdapter() 方法。
  3. 如果数据量不大,推荐使用 JsonSerializer 和 JsonDeserializer。
  4. 针对整个 Java Bean 的解析接管,可以使用 @JsonAdapter 注解。
    就这样吧,有问题在推文文末留言。
    本文对你有帮助吗?留言、点赞、转发是最大的支持,谢谢!

公众号后台回复成长『成长』,将会得到我准备的学习资料,也能回复『加群』,一起学习进步;你还能回复『提问』,向我发起提问。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

img
img

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
img

最后

对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

以下是今天给大家分享的一些独家干货:

获与成长,其余的都不重要,希望读者们能谨记这一点。

为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

以下是今天给大家分享的一些独家干货:

[外链图片转存中…(img-tN2JoHsu-1711568039094)]

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 17
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值