Gson全解析(中)-TypeAdapter的使用
TypeAdapter介绍
前面的Gson全解析(上)中我们理解并分别运用了JsonSerializer和JsonDeserializer进行JSON和java实体类之间的相互转化。这里利用TypeAdapter来更加高效的完成这个需求。
之前在上一篇文中提到的JsonSerializer
和JsonDeserializer解析的时候都利用到了一个中间件-JsonElement,比如下方的序列化过程。可以看到我们在把Java对象转化为JSON字符串的时候都会用到这个中间件JsonElement
而TypeAdapter的使用正是去掉了这个中间层,直接用流来解析数据,极大程度上提高了解析效率。
New applications should prefer TypeAdapter, whose streaming API is more efficient than this interface’s tree API.
应用中应当尽量使用TypeAdapter,它流式的API相比于之前的树形解析API将会更加高效。
TypeAdapter作为一个抽象类提供两个抽象方法。分别是write()和read()方法,也对应着序列化和反序列化。如下图所示:

下面就让我们来一起使用和了解TypeAdapter吧:
TypeAdapter实例
为了便于理解,这里还是统 一 一 下,采用和上面一篇文章同样的例子。Book.java实体类:
package com.javacreed.examples.gson.part1;
public class Book {
private String[] authors;
private String isbn;
private String title;
//为了代码简洁,这里移除getter和setter方法等
}
直接贴代码,具体序列化和反序列化的TypeAdapter类,这里是BookTypeAdapter.java:
package com.javacreed.examples.gson.part1;
import java.io.IOException;
import org.apache.commons.lang3.StringUtils;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
public class BookTypeAdapter extends TypeAdapter {
@Override
public Book read(final JsonReader in) throws IOException {
final Book book = new Book();
in.beginObject();
while (in.hasNext()) {
switch (in.nextName()) {
case "isbn":
book.setIsbn(in.nextString());
break;
case "title":
book.setTitle(in.nextString());
break;
case "authors":
book.setAuthors(in.nextString().split(";"));
break;
}
}
in.endObject();
return book;
}
@Override
public void write(final JsonWriter out, final Book book) throws IOException {
out.beginObject();
out.name("isbn").value(book.getIsbn());
out.name("title").value(book.getTitle());
out.name("authors").value(StringUtils.join(book.getAuthors(), ";"));
out.endObject();
}
}
同样这里设置TypeAdapter之后还是需要配置(注册),可以注意到的是gsonBuilder.registerTypeAdapter(xxx)方法进行注册在我们之前的JsonSerializer和JsonDeserializer中也有使用:
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Book.class, new BookTypeAdapter());
final Gson gson = gsonBuilder.create();
下面对两个write方法和read方法进行分别的阐述:
1 TypeAdapter中的write方法
write()方法中会传入JsonWriter,和需要被序列化的Book对象的实例,采用和PrintStream类似的方式 写入到JsonWriter中。
@Override
public void write(final JsonWriter out, final Book book) throws IOException {
out.beginObject();
out.name("isbn").value(book.getIsbn());
out.name("title").value(book.getTitle());
out.name("authors").value(StringUtils.join(book.getAuthors(), ";"));
out.endObject();
}
下面是上面代码的步骤:
out.beginObject()产生{,如果我们希望产生的是一个数组对象,对应的使用beginArray()out.name("isbn").value(book.getIsbn()); out.name("title").value(book.getTitle());分别获取book中的isbn和title字段并且设置给Json对象中的isbn和title。也就是说上面这段代码,会在json对象中产生:"isbn": "978-0321336781",
"title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",out.name("authors").value(StringUtils.join(book.getAuthors(), ";"));则会对应着:"authors": "Joshua Bloch;Neal Gafter"- 同理
out.endObject()则对应着} - 那么整个上面的代码也就会产生JSON对象:
{
"isbn": "978-0321336781",
"title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",
"authors": "Joshua Bloch;Neal Gafter"
} 这里需要注意的是,如果没有调用
out.endObject()产生},那么你的项目会报出JsonSyntaxException错误Exception in thread "main" com.google.gson.JsonSyntaxException: java.io.EOFException: End of input at line 4 column 40
at com.google.gson.Gson.fromJson(Gson.java:813)
at com.google.gson.Gson.fromJson(Gson.java:768)
at com.google.gson.Gson.fromJson(Gson.java:717)
at com.google.gson.Gson.fromJson(Gson.java:689)
at com.javacreed.examples.gson.part1.Main.main(Main.java:41)
Caused by: java.io.EOFException: End of input at line 4 column 40
at com.google.gson.stream.JsonReader.nextNonWhitespace(JsonReader.java:1377)
at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:471)
at com.google.gson.stream.JsonReader.hasNext(JsonReader.java:403)
at com.javacreed.examples.gson.part1.BookTypeAdapter.read(BookTypeAdapter.java:33)
at com.javacreed.examples.gson.part1.BookTypeAdapter.read(BookTypeAdapter.java:1)
at com.google.gson.Gson.fromJson(Gson.java:803)
... 4 more
2 TypeAdapter中的read方法
read()方法将会传入一个JsonReader对象实例并返回反序列化的对象。
@Override
public Book read(final JsonReader in) throws IOException {
final Book book = new Book();
in.beginObject();
while (in.hasNext()) {
switch (in.nextName()) {
case "isbn":
book.setIsbn(in.nextString());
break;
case "title":
book.setTitle(in.nextString());
break;
case "authors":
book.setAuthors(in.nextString().split(";"));
break;
}
}
in.endObject();
return book;
}
下面是这段代码的步骤:
- 同样是通过
in.beginObject();和in.endObject();对应解析{,} - 通过
while (in.hasNext()) {
switch (in.nextName()) {
}
}来完成每个
JsonElement的遍历,并且通过switch...case的方法获取Json对象中的键值对。并通过我们Book实体类的Setter方法进行设置。 while (in.hasNext()) {
switch (in.nextName()) {
case "isbn":
book.setIsbn(in.nextString());
break;
case "title":
book.setTitle(in.nextString());
break;
case "authors":
book.setAuthors(in.nextString().split(";"));
break;
}
}同样需要注意的是,如果没有执行
in.endObject(),将会出现JsonIOException的错误:Exception in thread "main" com.google.gson.JsonIOException: JSON document was not fully consumed.
at com.google.gson.Gson.assertFullConsumption(Gson.java:776)
at com.google.gson.Gson.fromJson(Gson.java:769)
at com.google.gson.Gson.fromJson(Gson.java:717)
at com.google.gson.Gson.fromJson(Gson.java:689)
at com.javacreed.examples.gson.part1.Main.main(Main.java:41)
下面给出使用TypeAdapter的完整代码:
package com.javacreed.examples.gson.part1;
import java.io.IOException;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class Main {
public static void main(final String[] args) throws IOException {
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Book.class, new BookTypeAdapter());
gsonBuilder.setPrettyPrinting();
final Gson gson = gsonBuilder.create();
final Book book = new Book();
book.setAuthors(new String[] { "Joshua Bloch", "Neal Gafter" });
book.setTitle("Java Puzzlers: Traps, Pitfalls, and Corner Cases");
book.setIsbn("978-0321336781");
final String json = gson.toJson(book);
System.out.println("Serialised");
System.out.println(json);
final Book parsedBook = gson.fromJson(json, Book.class);
System.out.println("\nDeserialised");
System.out.println(parsedBook);
}
}
对应的编译结果为:
Serialised
{
"isbn": "978-0321336781",
"title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",
"authors": "Joshua Bloch;Neal Gafter"
}
Deserialised
Java Puzzlers: Traps, Pitfalls, and Corner Cases [978-0321336781]
Written by:
>> Joshua Bloch
>> Neal Gafter
TypeAdapter处理简洁的JSON数据
为了简化JSON数据,其实我们上面的JSON数据可以这么写:
["978-0321336781","Java Puzzlers: Traps, Pitfalls, and Corner Cases","Joshua Bloch","Neal Gafter"]
可以看到的是,这样采用的直接是值的形式。当然这样操作简化了JSON数据但是可能就让整个数据的稳定性下降了许多的,你需要按照一定的顺序来解析这个数据。
对应的write和read方法如下:
@Override
public void write(final JsonWriter out, final Book book) throws IOException {
out.beginArray();
out.value(book.getIsbn());
out.value(book.getTitle());
for (final String author : book.getAuthors()) {
out.value(author);
}
out.endArray();
}
@Override
public Book read(final JsonReader in) throws IOException {
final Book book = new Book();
in.beginArray();
book.setIsbn(in.nextString());
book.setTitle(in.nextString());
final List authors = new ArrayList<>();
while (in.hasNext()) {
authors.add(in.nextString());
}
book.setAuthors(authors.toArray(new String[authors.size()]));
in.endArray();
return book;
}
这里的解析原理和上面一致,不再赘述。
TypeAdapter解析内置对象
(这里将nested objects翻译为内置对象,其实就是在Book类)
这里对上面的Book实体类进行修改如下,添加Author作者类,每本书可以有多个作者。
package com.javacreed.examples.gson.part3;
public class Book {
private Author[] authors;
private String isbn;
private String title;
class Author {
private int id;
private String name;
//为了代码简洁,这里移除getter和setter方法等
}
//为了代码简洁,这里移除getter和setter方法等
}
这里提供JSON对象,
{
"isbn": "978-0321336781",
"title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",
"authors": [
{
"id": 1,
"name": "Joshua Bloch"
},
{
"id": 2,
"name": "Neal Gafter"
}
]
}
下面分别展示write和read方法:
@Override
public void write(final JsonWriter out, final Book book) throws IOException {
out.beginObject();
out.name("isbn").value(book.getIsbn());
out.name("title").value(book.getTitle());
out.name("authors").beginArray();
for (final Author author : book.getAuthors()) {
out.beginObject();
out.name("id").value(author.getId());
out.name("name").value(author.getName());
out.endObject();
}
out.endArray();
out.endObject();
}
@Override
public Book read(final JsonReader in) throws IOException {
final Book book = new Book();
in.beginObject();
while (in.hasNext()) {
switch (in.nextName()) {
case "isbn":
book.setIsbn(in.nextString());
break;
case "title":
book.setTitle(in.nextString());
break;
case "authors":
in.beginArray();
final List authors = new ArrayList<>();
while (in.hasNext()) {
in.beginObject();
final Author author = new Author();
while (in.hasNext()) {
switch (in.nextName()) {
case "id":
author.setId(in.nextInt());
break;
case "name":
author.setName(in.nextString());
break;
}
}
authors.add(author);
in.endObject();
}
book.setAuthors(authors.toArray(new Author[authors.size()]));
in.endArray();
break;
}
}
in.endObject();
return book;
}
总结
TypeAdapter对JSON和Java对象之间的序列化和反序列化可以通过上面的方法进行操作。其实在解决解析内置对象的序列化和反序列化的时候我们也可以通过JsonDeserializer或者JsonSerializer进行操作,序列化过程如下:
@Override
public JsonElement serialize(final Book book, final Type typeOfSrc, final JsonSerializationContext context) {
final JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("isbn", book.getIsbn());
jsonObject.addProperty("title", book.getTitle());
final JsonElement jsonAuthros = context.serialize(book.getAuthors());
jsonObject.add("authors", jsonAuthros);
return jsonObject;
}
这里通过JsonSerializationContext提供的context对象直接解析,一定程度上提供了JSON对象序列化(反序列化)的一致性。
参考链接
翻译原文,根据原文做出了较大改动。
1 SIMPLE GSON EXAMPLE
2 GSON DESERIALISER EXAMPLE
3 GSON ANNOTATIONS EXAMPLE
4 GSON SERIALISER EXAMPLE
5 GSON TYPEADAPTER EXAMPLE
6 GSON TYPEADAPTER EXAMPLE SERIALISE LARGE OBJECTS
另附: 你真的会用Gson吗?Gson使用指南(一)系列文章
更多及时技术资讯,欢迎关注我的微博 :Anthony
本篇文章是基于Gson官方使用指导(Gson User Guide)以及Gson解析的优秀外文(来自http://www.javacreed.com/ )做出的一个翻译和归纳。
博客原链接:
Gson全解析(上)-Gson基础
Gson全解析(中)-TypeAdapter的使用
Gson全解析(下)-Gson性能分析
from: http://www.jianshu.com/p/8cc857583ff4
Gson全解析(中)-TypeAdapter的使用的更多相关文章
- Gson全解析(下)-Gson性能分析
前言 在之前的学习中,我们在Gson全解析(上)Gson使用的基础到分别运用了JsonSerializer和JsonDeserializer进行JSON和java实体类之间的相互转化. 在Gson全解 ...
- Gson全解析(上)-Gson基础
前言 最近在研究Retrofit中使用的Gson的时候,发现对Gson的一些深层次的概念和使用比较模糊,所以这里做一个知识点的归纳整理. Gson(又称Google Gson)是Google公司发布的 ...
- Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析
Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析 今天发一篇”水文”,可能很多读者都会表示不理解,不过我想把它作为并发序列文章中不可缺少的一块来介绍.本来以为花不了 ...
- CSS 中z-index全解析(摘自阿里西西)
z-index全解析 Z-index属性决定了一个HTML元素的层叠级别.元素层叠级别是相对于元素在Z轴上(与X轴Y轴相对照)的位置而言.一个更高的Z-index值意味着这个元素在叠层顺序中会更靠近顶 ...
- C# 嵌入dll 动软代码生成器基础使用 系统缓存全解析 .NET开发中的事务处理大比拼 C#之数据类型学习 【基于EF Core的Code First模式的DotNetCore快速开发框架】完成对DB First代码生成的支持 基于EF Core的Code First模式的DotNetCore快速开发框架 【懒人有道】在asp.net core中实现程序集注入
C# 嵌入dll 在很多时候我们在生成C#exe文件时,如果在工程里调用了dll文件时,那么如果不加以处理的话在生成的exe文件运行时需要连同这个dll一起转移,相比于一个单独干净的exe,这种形 ...
- 截取HTML中的JSON数据并利用GSON进行解析(Android)
截取HTML中的JSON数据并利用GSON进行解析(Android) 前言 最近在做的一个Android项目,需要自行搭建服务器,队友选择买了阿里云的服务器ESC产品,在数据获取上,我们采用了Andr ...
- Java并发指南13:Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析
Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析 转自https://www.javadoop.com/post/hashmap#toc7 部分内容转自 http: ...
- Java并发原理层面:ReentrantLock中lock()、unlock()全解析
一.前言 Java线程同步两种方式,synchronized关键字和Lock锁机制,其中,AQS队列就是Lock锁实现公平加锁的底层支持. 二.AQS源码对于lock.lock()的实现 2.1 AQ ...
- OkHttp使用全解析(转)。
Android系统提供了两种HTTP通信类,HttpURLConnection和HttpClient.关于HttpURLConnection和HttpClient的选择>>官方博客尽管Go ...
随机推荐
- SQL数据库基础知识
- CCF2014123集合竞价(C语言版)
问题描述 某股票交易所请你编写一个程序,根据开盘前客户提交的订单来确定某特定股票的开盘价和开盘成交量. 该程序的输入由很多行构成,每一行为一条记录,记录可能有以下几种: 1. buy p s 表示一个 ...
- LeetCode(45): 跳跃游戏 II
Hard! 题目描述: 给定一个非负整数数组,你最初位于数组的第一个位置. 数组中的每个元素代表你在该位置可以跳跃的最大长度. 你的目标是使用最少的跳跃次数到达数组的最后一个位置. 示例: 输入: [ ...
- lldp
https://wenku.baidu.com/view/b9d831f26294dd88d0d26b20.html
- day--16页面布局
后台页面布局 一.fixed布局 <!DOCTYPE html> <html lang="en"> <head> <meta ch ...
- python全栈开发day38-css三种引入方式、基础选择器、高级选择器、补充选择器
一.昨日内容回顾 div:分割整个网站,很多块 (1)排版标签 (2)块级标签 独占一行 可以设置高和宽,如果不设置宽高,默认是父盒子的宽 span: (1) 小区域 (2)文本标签 (3)在一行内显 ...
- Python 爬取生成中文词云以爬取知乎用户属性为例
代码如下: # -*- coding:utf-8 -*- import requests import pandas as pd import time import matplotlib.pyplo ...
- 记一次CPU占用率和load高的排查
前不久公司进行了一次大促,晚上值班.大促是从晚上8点多开始的,一开始流量慢慢的进来,观察了应用的各项指标,一切都是正常的,因为这是双11过后的第一次大促,想着用户的购买欲应该不会太强,所以我们的运维同 ...
- How to uninstall Maya on a Mac
How to uninstall Maya on a Mac Products and versions covered By: Support Jun 15 2018 SHARE ADD TO ...
- UVA 10976 分数拆分【暴力】
题目链接:https://vjudge.net/contest/210334#problem/C 题目大意: It is easy to see that for every fraction in ...