Gson全解析(中)
Posted CameloeAnthony
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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"
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对象序列化(反序列化)的一致性。
以上是关于Gson全解析(中)的主要内容,如果未能解决你的问题,请参考以下文章