TypeAdapter介绍

前面的Gson全解析(上)中我们理解并分别运用了JsonSerializer和JsonDeserializer进行JSON和java实体类之间的相互转化。这里利用TypeAdapter来更加高效的完成这个需求。

之前在上一篇文中提到的JsonSerializer
JsonDeserializer解析的时候都利用到了一个中间件-JsonElement,比如下方的序列化过程。可以看到我们在把Java对象转化为JSON字符串的时候都会用到这个中间件JsonElement

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)方法进行注册在我们之前的JsonSerializerJsonDeserializer中也有使用:

    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数据但是可能就让整个数据的稳定性下降了许多的,你需要按照一定的顺序来解析这个数据。
对应的writeread方法如下:

  @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对象序列化(反序列化)的一致性。


参考链接

翻译原文,根据原文做出了较大改动。
SIMPLE GSON EXAMPLE
GSON DESERIALISER EXAMPLE
GSON ANNOTATIONS EXAMPLE
GSON SERIALISER EXAMPLE
GSON TYPEADAPTER EXAMPLE
GSON TYPEADAPTER EXAMPLE SERIALISE LARGE OBJECTS

另附: 你真的会用Gson吗?Gson使用指南(一)系列文章

更多及时技术资讯,欢迎关注我的微博 :Anthony

gson github地址google/gson

本篇文章是基于Gson官方使用指导(Gson User Guide)以及Gson解析的优秀外文(来自http://www.javacreed.com/ )做出的一个翻译和归纳。
博客原链接:
Gson全解析(上)-Gson基础
Gson全解析(中)-TypeAdapter的使用
Gson全解析(下)-Gson性能分析

from: http://www.jianshu.com/p/8cc857583ff4

Gson全解析(中)-TypeAdapter的使用的更多相关文章

  1. Gson全解析(下)-Gson性能分析

    前言 在之前的学习中,我们在Gson全解析(上)Gson使用的基础到分别运用了JsonSerializer和JsonDeserializer进行JSON和java实体类之间的相互转化. 在Gson全解 ...

  2. Gson全解析(上)-Gson基础

    前言 最近在研究Retrofit中使用的Gson的时候,发现对Gson的一些深层次的概念和使用比较模糊,所以这里做一个知识点的归纳整理. Gson(又称Google Gson)是Google公司发布的 ...

  3. Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析

    Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析 今天发一篇”水文”,可能很多读者都会表示不理解,不过我想把它作为并发序列文章中不可缺少的一块来介绍.本来以为花不了 ...

  4. CSS 中z-index全解析(摘自阿里西西)

    z-index全解析 Z-index属性决定了一个HTML元素的层叠级别.元素层叠级别是相对于元素在Z轴上(与X轴Y轴相对照)的位置而言.一个更高的Z-index值意味着这个元素在叠层顺序中会更靠近顶 ...

  5. 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,这种形 ...

  6. 截取HTML中的JSON数据并利用GSON进行解析(Android)

    截取HTML中的JSON数据并利用GSON进行解析(Android) 前言 最近在做的一个Android项目,需要自行搭建服务器,队友选择买了阿里云的服务器ESC产品,在数据获取上,我们采用了Andr ...

  7. Java并发指南13:Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析

    Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析 转自https://www.javadoop.com/post/hashmap#toc7 部分内容转自 http: ...

  8. Java并发原理层面:ReentrantLock中lock()、unlock()全解析

    一.前言 Java线程同步两种方式,synchronized关键字和Lock锁机制,其中,AQS队列就是Lock锁实现公平加锁的底层支持. 二.AQS源码对于lock.lock()的实现 2.1 AQ ...

  9. OkHttp使用全解析(转)。

    Android系统提供了两种HTTP通信类,HttpURLConnection和HttpClient.关于HttpURLConnection和HttpClient的选择>>官方博客尽管Go ...

随机推荐

  1. python 全栈开发,Day77(图书管理系统)

    一.图书管理系统 完整代码链接: https://github.com/py3study/bms_multi 本项目使用session来实现一个简单的图书管理系统 未登录不允许访问后台: 直接访问后台 ...

  2. MVC开发中的常见错误-06-"无法在发送 HTTP 标头之后进行重定向。"

    通过监视可以看到: 原来是跳转到登录页面后,登录页面中又发送了一个GeMneuItems的请求,用于加载页面图片

  3. HTTP协议特点

    1 HTTP协议特点      1)客户端->服务端(请求request)有三部份         a)请求行--请求行用于描述客户端的请求方式.请求的资源名称,以及使用的HTTP协议版本号 请 ...

  4. Django ORM中使用update_or_create功能再解

    以前,我解过这个问题,现在百度搜索,发了像也只能找到我这个帖子. https://www.cnblogs.com/aguncn/p/4922654.html 今天,看了看官方文档,关于这个update ...

  5. java利用Jsch实现在windows平台远程操作linux服务器

    说明:exec用于执行命令:sftp用于文件处理 package com.wyg.simple; import java.io.BufferedReader; import java.io.File; ...

  6. PHP浮点数的精确计算BCMath

    Php: BCMath bc是Binary Calculator的缩写.bc*函数的参数都是操作数加上一个可选的 [int scale],比如string bcadd(string $left_ope ...

  7. [HDU] 5306 Gorgeous Sequence [区间取min&求和&求max]

    题解: 线段树维护区间取min求和求max 维护最小值以及个数,次小值 标记清除时,分情况讨论 当lazy>max1 退出 当max1>lazy>max2(注意不要有等号) 更新 否 ...

  8. BZOJ1192 [HNOI2006]鬼谷子的钱袋 数学推理

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1192 题意概括 把一个数m拆成很多数字. 问至少拆成多少个数字,1~m中的所有数字才可以用这些数字 ...

  9. 033 关于YARN的HA

    一:准备 1.规划 namenode               namenode ZKFC ZKFC journalnode        journalnode               jou ...

  10. PHP函数之array_chunk

    有时候需要对数组进行按分页处理,之前的做法是计算出数组大小,按分页计算出偏移量,再从起始偏移量处开始遍历页大小个数据.现在不用这么麻烦了,原来PHP函数里有个现成的函数array_chunk可以配合我 ...