Gson全解析(中)
来源:互联网 发布:linux ssh安装 编辑:程序博客网 时间:2024/05/20 09:20
gson github地址google/gson
本篇文章是基于Gson官方使用指导(Gson User Guide)以及Gson解析的优秀外文(来自http://www.javacreed.com/ )做出的一个翻译和归纳。
博客原链接:
Gson全解析(上)
Gson全解析(中)
TypeAdapter介绍
前面的Gson全解析(上)中我们理解并分别运用了JsonSerializer和JsonDeserializer进行JSON和java实体类之间的相互转化。这里利用TypeAdapter
来更加高效的完成同样的这个功能。
之前在上一篇文中提到的JsonSerializer
和 JsonDeserializer
解析的时候都利用到了一个中间件-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实例
为了便于理解,这里还是统一一下,采用和上面一篇文章同样的例子。 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)
下面给出完整代码:
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"}DeserialisedJava 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对象序列化(反序列化)的一致性。
- Gson全解析(中)
- Gson全解析(上)-Gson基础
- Gson全解析(上)
- Gson全解析(下)-Gson性能分析
- Android中Json的全解析(JSONObject,JSONArray,Gson,Jackson)
- Gson项目使用全解析
- google 的 Gson 全解析
- Gson全解析之一:JsonReader的beginObject()
- android中用Gson解析json全解析
- android中Gson解析
- Gson解析(详解)
- Android中Gson解析json
- gson解析带中括号
- Gson解析带中括号
- Gson全解析之二:JsonReader的其它方法
- Gson解析(实战开发)
- json数据解析(gson)
- Android中使用Gson解析JSON数据
- 64位内核能支持32位的应用?
- iOS9全面拥抱64位 iPhone4S/5/5C悲剧了
- 14. 试用vSphere 6(三):安装vCenter 6(独立数据库)之:域控服务器安装与配置
- java函数调用web service
- css实现鼠标悬停图片放大显示
- Gson全解析(中)
- 解决精度损失问题
- Kafka - SQL 引擎分享
- cf - 629D Babaei and Birthday Cake(DP+线段树维护)
- hadoop-2.5.0-cdh5.3.0 HA在线升级
- 操作系统日志分析中常见的搜索条目 20160715
- 卷积理解以及在数字图像处理中的应用
- idea 15注册方法 破解方法 注册码
- Javassist 代码转换