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实体类:

  1. package com.javacreed.examples.gson.part1;
  2. public class Book {
  3. private String[] authors;
  4. private String isbn;
  5. private String title;
  6. //为了代码简洁,这里移除getter和setter方法等
  7. }

直接贴代码,具体序列化和反序列化的TypeAdapter类,这里是BookTypeAdapter.java

  1. package com.javacreed.examples.gson.part1;
  2. import java.io.IOException;
  3. import org.apache.commons.lang3.StringUtils;
  4. import com.google.gson.TypeAdapter;
  5. import com.google.gson.stream.JsonReader;
  6. import com.google.gson.stream.JsonWriter;
  7. public class BookTypeAdapter extends TypeAdapter {
  8. @Override
  9. public Book read(final JsonReader in) throws IOException {
  10. final Book book = new Book();
  11. in.beginObject();
  12. while (in.hasNext()) {
  13. switch (in.nextName()) {
  14. case "isbn":
  15. book.setIsbn(in.nextString());
  16. break;
  17. case "title":
  18. book.setTitle(in.nextString());
  19. break;
  20. case "authors":
  21. book.setAuthors(in.nextString().split(";"));
  22. break;
  23. }
  24. }
  25. in.endObject();
  26. return book;
  27. }
  28. @Override
  29. public void write(final JsonWriter out, final Book book) throws IOException {
  30. out.beginObject();
  31. out.name("isbn").value(book.getIsbn());
  32. out.name("title").value(book.getTitle());
  33. out.name("authors").value(StringUtils.join(book.getAuthors(), ";"));
  34. out.endObject();
  35. }
  36. }

同样这里设置TypeAdapter之后还是需要配置(注册),可以注意到的是gsonBuilder.registerTypeAdapter(xxx)方法进行注册在我们之前的JsonSerializerJsonDeserializer中也有使用:

  1. final GsonBuilder gsonBuilder = new GsonBuilder();
  2. gsonBuilder.registerTypeAdapter(Book.class, new BookTypeAdapter());
  3. final Gson gson = gsonBuilder.create();

下面对两个write方法和read方法进行分别的阐述:

1 TypeAdapter中的write方法

write()方法中会传入JsonWriter,和需要被序列化的Book对象的实例,采用和PrintStream类似的方式 写入到JsonWriter中。

  1. @Override
  2. public void write(final JsonWriter out, final Book book) throws IOException {
  3. out.beginObject();
  4. out.name("isbn").value(book.getIsbn());
  5. out.name("title").value(book.getTitle());
  6. out.name("authors").value(StringUtils.join(book.getAuthors(), ";"));
  7. out.endObject();
  8. }

下面是上面代码的步骤:

  • out.beginObject()产生{,如果我们希望产生的是一个数组对象,对应的使用beginArray()
  • out.name("isbn").value(book.getIsbn()); out.name("title").value(book.getTitle());分别获取book中的isbn和title字段并且设置给Json对象中的isbn和title。也就是说上面这段代码,会在json对象中产生:
    1. "isbn": "978-0321336781",
    2. "title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",
  • out.name("authors").value(StringUtils.join(book.getAuthors(), ";"));则会对应着:
    1. "authors": "Joshua Bloch;Neal Gafter"
  • 同理 out.endObject()则对应着}
  • 那么整个上面的代码也就会产生JSON对象:
    1. {
    2. "isbn": "978-0321336781",
    3. "title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",
    4. "authors": "Joshua Bloch;Neal Gafter"
    5. }
  • 这里需要注意的是,如果没有调用 out.endObject()产生},那么你的项目会报出 JsonSyntaxException错误

    1. Exception in thread "main" com.google.gson.JsonSyntaxException: java.io.EOFException: End of input at line 4 column 40
    2. at com.google.gson.Gson.fromJson(Gson.java:813)
    3. at com.google.gson.Gson.fromJson(Gson.java:768)
    4. at com.google.gson.Gson.fromJson(Gson.java:717)
    5. at com.google.gson.Gson.fromJson(Gson.java:689)
    6. at com.javacreed.examples.gson.part1.Main.main(Main.java:41)
    7. Caused by: java.io.EOFException: End of input at line 4 column 40
    8. at com.google.gson.stream.JsonReader.nextNonWhitespace(JsonReader.java:1377)
    9. at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:471)
    10. at com.google.gson.stream.JsonReader.hasNext(JsonReader.java:403)
    11. at com.javacreed.examples.gson.part1.BookTypeAdapter.read(BookTypeAdapter.java:33)
    12. at com.javacreed.examples.gson.part1.BookTypeAdapter.read(BookTypeAdapter.java:1)
    13. at com.google.gson.Gson.fromJson(Gson.java:803)
    14. ... 4 more
2 TypeAdapter中的read方法

read()方法将会传入一个JsonReader对象实例并返回反序列化的对象。

  1. @Override
  2. public Book read(final JsonReader in) throws IOException {
  3. final Book book = new Book();
  4. in.beginObject();
  5. while (in.hasNext()) {
  6. switch (in.nextName()) {
  7. case "isbn":
  8. book.setIsbn(in.nextString());
  9. break;
  10. case "title":
  11. book.setTitle(in.nextString());
  12. break;
  13. case "authors":
  14. book.setAuthors(in.nextString().split(";"));
  15. break;
  16. }
  17. }
  18. in.endObject();
  19. return book;
  20. }

下面是这段代码的步骤:

  • 同样是通过in.beginObject();in.endObject();对应解析{,}
  • 通过
    1. while (in.hasNext()) {
    2. switch (in.nextName()) {
    3. }
    4. }

    来完成每个JsonElement的遍历,并且通过switch...case的方法获取Json对象中的键值对。并通过我们Book实体类Setter方法进行设置。

    1. while (in.hasNext()) {
    2. switch (in.nextName()) {
    3. case "isbn":
    4. book.setIsbn(in.nextString());
    5. break;
    6. case "title":
    7. book.setTitle(in.nextString());
    8. break;
    9. case "authors":
    10. book.setAuthors(in.nextString().split(";"));
    11. break;
    12. }
    13. }
  • 同样需要注意的是,如果没有执行in.endObject(),将会出现JsonIOException的错误:

    1. Exception in thread "main" com.google.gson.JsonIOException: JSON document was not fully consumed.
    2. at com.google.gson.Gson.assertFullConsumption(Gson.java:776)
    3. at com.google.gson.Gson.fromJson(Gson.java:769)
    4. at com.google.gson.Gson.fromJson(Gson.java:717)
    5. at com.google.gson.Gson.fromJson(Gson.java:689)
    6. at com.javacreed.examples.gson.part1.Main.main(Main.java:41)

下面给出使用TypeAdapter的完整代码:

  1. package com.javacreed.examples.gson.part1;
  2. import java.io.IOException;
  3. import com.google.gson.Gson;
  4. import com.google.gson.GsonBuilder;
  5. public class Main {
  6. public static void main(final String[] args) throws IOException {
  7. final GsonBuilder gsonBuilder = new GsonBuilder();
  8. gsonBuilder.registerTypeAdapter(Book.class, new BookTypeAdapter());
  9. gsonBuilder.setPrettyPrinting();
  10. final Gson gson = gsonBuilder.create();
  11. final Book book = new Book();
  12. book.setAuthors(new String[] { "Joshua Bloch", "Neal Gafter" });
  13. book.setTitle("Java Puzzlers: Traps, Pitfalls, and Corner Cases");
  14. book.setIsbn("978-0321336781");
  15. final String json = gson.toJson(book);
  16. System.out.println("Serialised");
  17. System.out.println(json);
  18. final Book parsedBook = gson.fromJson(json, Book.class);
  19. System.out.println("\nDeserialised");
  20. System.out.println(parsedBook);
  21. }
  22. }

对应的编译结果为:

  1. Serialised
  2. {
  3. "isbn": "978-0321336781",
  4. "title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",
  5. "authors": "Joshua Bloch;Neal Gafter"
  6. }
  7. Deserialised
  8. Java Puzzlers: Traps, Pitfalls, and Corner Cases [978-0321336781]
  9. Written by:
  10. >> Joshua Bloch
  11. >> Neal Gafter

TypeAdapter处理简洁的JSON数据

为了简化JSON数据,其实我们上面的JSON数据可以这么写:

  1. ["978-0321336781","Java Puzzlers: Traps, Pitfalls, and Corner Cases","Joshua Bloch","Neal Gafter"]

可以看到的是,这样采用的直接是值的形式。当然这样操作简化了JSON数据但是可能就让整个数据的稳定性下降了许多的,你需要按照一定的顺序来解析这个数据。
对应的writeread方法如下:

  1. @Override
  2. public void write(final JsonWriter out, final Book book) throws IOException {
  3. out.beginArray();
  4. out.value(book.getIsbn());
  5. out.value(book.getTitle());
  6. for (final String author : book.getAuthors()) {
  7. out.value(author);
  8. }
  9. out.endArray();
  10. }
  1. @Override
  2. public Book read(final JsonReader in) throws IOException {
  3. final Book book = new Book();
  4. in.beginArray();
  5. book.setIsbn(in.nextString());
  6. book.setTitle(in.nextString());
  7. final List authors = new ArrayList<>();
  8. while (in.hasNext()) {
  9. authors.add(in.nextString());
  10. }
  11. book.setAuthors(authors.toArray(new String[authors.size()]));
  12. in.endArray();
  13. return book;
  14. }

这里的解析原理和上面一致,不再赘述。


TypeAdapter解析内置对象

(这里将nested objects翻译为内置对象,其实就是在Book类)

这里对上面的Book实体类进行修改如下,添加Author作者类,每本书可以有多个作者。

  1. package com.javacreed.examples.gson.part3;
  2. public class Book {
  3. private Author[] authors;
  4. private String isbn;
  5. private String title;
  6. class Author {
  7. private int id;
  8. private String name;
  9. //为了代码简洁,这里移除getter和setter方法等
  10. }
  11. //为了代码简洁,这里移除getter和setter方法等
  12. }

这里提供JSON对象,

  1. {
  2. "isbn": "978-0321336781",
  3. "title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",
  4. "authors": [
  5. {
  6. "id": 1,
  7. "name": "Joshua Bloch"
  8. },
  9. {
  10. "id": 2,
  11. "name": "Neal Gafter"
  12. }
  13. ]
  14. }

下面分别展示write和read方法:

  1. @Override
  2. public void write(final JsonWriter out, final Book book) throws IOException {
  3. out.beginObject();
  4. out.name("isbn").value(book.getIsbn());
  5. out.name("title").value(book.getTitle());
  6. out.name("authors").beginArray();
  7. for (final Author author : book.getAuthors()) {
  8. out.beginObject();
  9. out.name("id").value(author.getId());
  10. out.name("name").value(author.getName());
  11. out.endObject();
  12. }
  13. out.endArray();
  14. out.endObject();
  15. }
  1. @Override
  2. public Book read(final JsonReader in) throws IOException {
  3. final Book book = new Book();
  4. in.beginObject();
  5. while (in.hasNext()) {
  6. switch (in.nextName()) {
  7. case "isbn":
  8. book.setIsbn(in.nextString());
  9. break;
  10. case "title":
  11. book.setTitle(in.nextString());
  12. break;
  13. case "authors":
  14. in.beginArray();
  15. final List authors = new ArrayList<>();
  16. while (in.hasNext()) {
  17. in.beginObject();
  18. final Author author = new Author();
  19. while (in.hasNext()) {
  20. switch (in.nextName()) {
  21. case "id":
  22. author.setId(in.nextInt());
  23. break;
  24. case "name":
  25. author.setName(in.nextString());
  26. break;
  27. }
  28. }
  29. authors.add(author);
  30. in.endObject();
  31. }
  32. book.setAuthors(authors.toArray(new Author[authors.size()]));
  33. in.endArray();
  34. break;
  35. }
  36. }
  37. in.endObject();
  38. return book;
  39. }

总结

TypeAdapter对JSON和Java对象之间的序列化和反序列化可以通过上面的方法进行操作。其实在解决解析内置对象的序列化和反序列化的时候我们也可以通过JsonDeserializer或者JsonSerializer进行操作,序列化过程如下:

  1. @Override
  2. public JsonElement serialize(final Book book, final Type typeOfSrc, final JsonSerializationContext context) {
  3. final JsonObject jsonObject = new JsonObject();
  4. jsonObject.addProperty("isbn", book.getIsbn());
  5. jsonObject.addProperty("title", book.getTitle());
  6. final JsonElement jsonAuthros = context.serialize(book.getAuthors());
  7. jsonObject.add("authors", jsonAuthros);
  8. return jsonObject;
  9. }

这里通过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. 欧拉函数,打表求欧拉函数poj3090

    欧拉函数 φ(n) 定义:[1,N]中与N互质的数的个数 //互质与欧拉函数 /* 求欧拉函数 按欧拉函数计算公式,只要分解质因数即可 */ int phi(int n){ int ans=n; ;i ...

  2. 性能测试三十二:监控之Java线程监控

    线程的五种状态 * 新建:new * 运行:runnable * 等待:waitting(无限期等待),timed waitting(限期等待) * 阻塞:blocked * 结束:terminate ...

  3. JQuery中jsCharts图表插件(十)

    一:1.jsCharts图表插件 注意:从官方下来的例子都没指定页面编码,在这种情况下,浏览器就会使用默认设置中文编码:GB2312,GBK等:导致无法执行. 请在html代码中的<head&g ...

  4. vim的基本用法

  5. Entity Framework解决sql 条件拼接,完美解决 解决 不支持 LINQ 表达式节点类型“Invoke”【转】

    传统的操作数据库方式,筛选数据需要用StringBuilder拼接一大堆的WHERE子句. 在Entity Framework中,代码稍有不慎就会造成巨大性能消耗,如: using(var db=ne ...

  6. Android使用帧动画内存溢出解决方法

    Android使用帧动画内存溢出解决方法https://blog.csdn.net/daitu_liang/article/details/52336015https://blog.csdn.net/ ...

  7. [转] iOS开发工具——网络封包分析工具Charles

    简介 Charles是在Mac下常用的截取网络封包的工具,在做iOS开发时,我们为了调试与服务器端的网络通讯协议,常常需要截取网络封包来分析.Charles通过将自己设置成系统的网络访问代理服务器,使 ...

  8. 用Delphi从内存流中判断图片格式

    https://blog.csdn.net/my98800/article/details/53536774 废话不多说了,利用内存流来判断文件的格式,其实判断文件的前几个字节就可以简单的判断这个文件 ...

  9. Python中List的append引用赋值问题处理

    Python中的对象之间赋值时是按引用传递的,如果需要拷贝对象,需要使用标准库中的copy模块. 1. copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象. 2. copy.deep ...

  10. #9 //[SDOI2017]新生舞会

    题解: 分数规划+费用流 常数巨大开o2加inline加register还是不行 我也不知道为什么 代码: #include <bits/stdc++.h> using namespace ...