前言

最近在研究Retrofit中使用的Gson的时候,发现对Gson的一些深层次的概念和使用比较模糊,所以这里做一个知识点的归纳整理。

Gson(又称Google Gson)是Google公司发布的一个开放源代码的Java库,主要用途为序列化Java对象为JSON字符串,或反序列化JSON字符串成Java对象。而JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成,广泛应用于各种数据的交互中,尤其是服务器与客户端的交互。


基本概念

  • Serialization:序列化,使Java对象到Json字符串的过程。
  • Deserialization:反序列化,字符串转换成Java对象。
  • JSON数据中的JsonElement有下面这四种类型:
    JsonPrimitive —— 例如一个字符串或整型
    JsonObject—— 一个以 JsonElement 名字(类型为 String)作为索引的集合。也就是说可以把 JsonObject 看作值为 JsonElement 的键值对集合。
    JsonArray—— JsonElement 的集合。注意数组的元素可以是四种类型中的任意一种,或者混合类型都支持。
    JsonNull—— 值为null

Gson解决的问题

  1. 提供一种像toString()和构造方法的很简单的机制,来实现Java 对象和Json之间的互相转换。

  2. 允许已经存在的无法改变的对象,转换成Json,或者Json转换成已存在的对象。

  3. 允许自定义对象的表现形式

  4. 支持任意的复杂对象

  5. 能够生成可压缩和可读的Json的字符串输出。


Gson处理对象的几个重要点

1 推荐把成员变量都声明称private的

2 没有必要用注解(@Expose 注解)指明某个字段是否会被序列化或者反序列化,所有包含在当前类(包括父类)中的字段都应该默认被序列化或者反序列化

3 如果某个字段被 transient 这个Java关键词修饰,就不会被序列化或者反序列化

4 下面的实现方式能够正确的处理null
1)当序列化的时候,如果对象的某个字段为null,是不会输出到Json字符串中的。
2)当反序列化的时候,某个字段在Json字符串中找不到对应的值,就会被赋值为null

5 如果一个字段是 synthetic
的,他会被忽视,也即是不应该被序列化或者反序列化

6 内部类(或者anonymous class(匿名类),或者local class(局部类,可以理解为在方法内部声明的类))的某个字段和外部类的某个字段一样的话,就会被忽视,不会被序列化或者反序列化


Gson中的一些注解

1 @SerializedName注解

该注解能指定该字段在JSON中对应的字段名称

public class Box {

  @SerializedName("w")
private int width; @SerializedName("h")
private int height; @SerializedName("d")
private int depth; // Methods removed for brevity
}

也就是说{"w":10,"h":20,"d":30} 这个JSON 字符串能够被解析到上面的width,height和depth字段中。

2 @Expose注解

该注解能够指定该字段是否能够序列化或者反序列化,默认的是都支持(true)。

public class Account {

  @Expose(deserialize = false)
private String accountNumber; @Expose
private String iban; @Expose(serialize = false)
private String owner; @Expose(serialize = false, deserialize = false)
private String address; private String pin;
}

需要注意的通过 builder.excludeFieldsWithoutExposeAnnotation()方法是该注解生效。

  final GsonBuilder builder = new GsonBuilder();
builder.excludeFieldsWithoutExposeAnnotation();
final Gson gson = builder.create();
3 @Since和@Until注解

Since代表“自从”,Until 代表”一直到”。它们都是针对该字段生效的版本。比如说@Since(1.2)代表从版本1.2之后才生效,@Until(0.9)代表着在0.9版本之前都是生效的。

public class SoccerPlayer {

  private String name;

  @Since(1.2)
private int shirtNumber; @Until(0.9)
private String country; private String teamName; // Methods removed for brevity
}

也就是说我们利用方法builder.setVersion(1.0)定义版本1.0,如下:

 final GsonBuilder builder = new GsonBuilder();
builder.setVersion(1.0); final Gson gson = builder.create(); final SoccerPlayer account = new SoccerPlayer();
account.setName("Albert Attard");
account.setShirtNumber(10); // Since version 1.2
account.setTeamName("Zejtun Corinthians");
account.setCountry("Malta"); // Until version 0.9 final String json = gson.toJson(account);
System.out.printf("Serialised (version 1.0)%n %s%n", json);

由于shirtNumbercountry作用版本分别是1.2之后,和0.9之前,所以在这里都不会得到序列化,所以输出结果是:

Serialised (version 1.0)
{"name":"Albert Attard","teamName":"Zejtun Corinthians"}

Gson 序列化

英文Serialize和format都对应序列化,这是一个Java对象到JSON字符串的过程。
接着看一个例子,下面分别是java类和以及我们期望的JSON数据:

public class Book {
private String[] authors;
private String isbn10;
private String isbn13;
private String title;
//为了代码简洁,这里移除getter和setter方法等 }
{
"title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",
"isbn-10": "032133678X",
"isbn-13": "978-0321336781",
"authors": [
"Joshua Bloch",
"Neal Gafter"
]
}

你肯定能发现JSON数据中出现了isbn-10isbn-13, 我们怎么把字段数据isbn10isbn13转化为JSON数据需要的isbn-10isbn-13,Gson当然为我们提供了对应的解决方案

1 序列化方案1

采用上面提到的@SerializedName注解。

public class Book {
private String[] authors; @SerializedName("isbn-10")
private String isbn10; @SerializedName("isbn-13")
private String isbn13;
private String title;
//为了代码简洁,这里移除getter和setter方法等 }
2 序列化方案2

利用JsonSerializer

public class BookSerialiser implements JsonSerializer {

    @Override
public JsonElement serialize(final Book book, final Type typeOfSrc, final JsonSerializationContext context) { final JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("title", book.getTitle());
jsonObject.addProperty("isbn-10", book.getIsbn10());
jsonObject.addProperty("isbn-13", book.getIsbn13()); final JsonArray jsonAuthorsArray = new JsonArray();
for (final String author : book.getAuthors()) {
final JsonPrimitive jsonAuthor = new JsonPrimitive(author);
jsonAuthorsArray.add(jsonAuthor);
}
jsonObject.add("authors", jsonAuthorsArray); return jsonObject;
}
}

下面对序列化过程进行大致的分析:

  • JsonSerializer是一个接口,我们需要提供自己的实现,来满足自己的序列化要求。

    public interface JsonSerializer<T> {
    
    /**
    *Gson 会在解析指定类型T数据的时候触发当前回调方法进行序列化
    *
    * @param T 需要转化为Json数据的类型,对应上面的Book
    * @return 返回T指定的类对应JsonElement
    */
    public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context);
    }
  • 首先在上面的代码中,我们需要创建的是一个JsonElement对象,这里对应Book是一个对象,所以创建一个JsonObject类型。
    final JsonObject jsonObject = new JsonObject();
  • 然后我们将相应字段里面的数据填充到jsonObject里面。
    jsonObject.addProperty...
    jsonObject.add...

    下面是jsonObject中的添加方法:

  • 所以最后返回的还是一个JsonElement 类型,这里对应的是jsonObject。完成了javaBean->JSON数据的转化。

  • 同样需要配置,

    // Configure GSON
    final GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(Book.class, new BookSerialiser());
    gsonBuilder.setPrettyPrinting();
    final Gson gson = gsonBuilder.create(); final Book javaPuzzlers = new Book();
    javaPuzzlers.setTitle("Java Puzzlers: Traps, Pitfalls, and Corner Cases");
    javaPuzzlers.setIsbn10("032133678X");
    javaPuzzlers.setIsbn13("978-0321336781");
    javaPuzzlers.setAuthors(new String[] { "Joshua Bloch", "Neal Gafter" }); // Format to JSON
    final String json = gson.toJson(javaPuzzlers);
    System.out.println(json);

    ,这里对应的是
    gsonBuilder.registerTypeAdapter(Book.class, new BookSerialiser())方法进行JsonSerializer的配置。在上面例子中,通过调用gsonBuilder.setPrettyPrinting();方法还告诉了 Gson 对生成的 JSON 对象进行格式化


Gson 反序列化

英文parse和deserialise对应反序列化,这是一个字符串转换成Java对象的过程。
我们同样采用上面一小节的代码片段,只不过现在我们需要做的是将:

{
"title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",
"isbn-10": "032133678X",
"isbn-13": "978-0321336781",
"authors": [
"Joshua Bloch",
"Neal Gafter"
]
}

转化为对应的Book实体类,

1 反序列化方案1

利用@SerializedName 注解
也就是说我们的实体类Book.java可以这么写:

public class Book {
private String[] authors; @SerializedName("isbn-10")
private String isbn10; @SerializedName(value = "isbn-13", alternate = {"isbn13","isbn.13"})
private String isbn13;
private String title;
//为了代码简洁,这里移除getter和setter方法等 }

可以看到这里我们在@SerializedName 注解使用了一个valuealternate字段,value也就是默认的字段,对序列化和反序列化都有效,alternate只有反序列化才有效果。也就是说一般服务器返回给我们JSON数据的时候可能同样的一个图片,表示"image","img","icon"等,我们利用@SerializedName 中的alternate字段就能解决这个问题,全部转化为我们实体类中的图片字段。

2 反序列化方案2

我们在序列化的时候使用的是JsonSerialize ,这里对应使用JsonDeserializer
我们将解析到的json数据传递给Book的setter方法即可。

public class BookDeserializer implements JsonDeserializer<Book> {

  @Override
public Book deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
throws JsonParseException {
final JsonObject jsonObject = json.getAsJsonObject(); final JsonElement jsonTitle = jsonObject.get("title");
final String title = jsonTitle.getAsString(); final String isbn10 = jsonObject.get("isbn-10").getAsString();
final String isbn13 = jsonObject.get("isbn-13").getAsString(); final JsonArray jsonAuthorsArray = jsonObject.get("authors").getAsJsonArray();
final String[] authors = new String[jsonAuthorsArray.size()];
for (int i = 0; i < authors.length; i++) {
final JsonElement jsonAuthor = jsonAuthorsArray.get(i);
authors[i] = jsonAuthor.getAsString();
} final Book book = new Book();
book.setTitle(title);
book.setIsbn10(isbn10);
book.setIsbn13(isbn13);
book.setAuthors(authors);
return book;
}
}

和Gson序列化章节一样,我们这里接着分析我们是怎么将JSON数据解析(反序列化)为实体类的:

  • 因为我们可以发现上面的JSON数据是一个{}大括号包围的,也就意味着这是一个Json对象。所以首先我们通过
    final JsonObject jsonObject = json.getAsJsonObject();将我们的JsonElement转化为JsonObject
  • 通过jsonObject.get("xxx").getAsString()的形式获取相应String的值
  • 通过jsonObject.get("xx").getAsJsonArray();获取相应的json数组,并遍历出其中的相应字段值
  • 通过setter方法,将获取到的值设置给Book类。
  • 最终返回的是 Book的对象实例。完成了JSON->javaBean的转化
  • 同样需要配置
  • 关于从本地流中读取Json数据可以使用 InputStreamReader完成

    // Configure Gson
    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(Book.class, new BookDeserializer());
    Gson gson = gsonBuilder.create(); // The JSON data
    try(Reader reader = new InputStreamReader(Main.class.getResourceAsStream("/part1/sample.json"), "UTF-8")){ // Parse JSON to Java
    Book book = gson.fromJson(reader, Book.class);
    System.out.println(book);
    }

参考链接

翻译原文,根据原文做出了较大改动。
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/fc5c9cdf3aab

Gson全解析(上)-Gson基础的更多相关文章

  1. Gson全解析(中)-TypeAdapter的使用

    TypeAdapter介绍 前面的Gson全解析(上)中我们理解并分别运用了JsonSerializer和JsonDeserializer进行JSON和java实体类之间的相互转化.这里利用TypeA ...

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

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

  3. Android JSON解析库Gson和Fast-json的使用对比和图书列表小案例

    Android JSON解析库Gson和Fast-json的使用对比和图书列表小案例 继上篇json解析,我用了原生的json解析,但是在有些情况下我们不得不承认,一些优秀的json解析框架确实十分的 ...

  4. 使用gson在解析unicode时遇到的问题

    之前在用gson解析的时候未记录下来,所以今天做一个小的总结, 比如遇到像这种"\u003d"的unicode的字符,我们想解码这个字符,用gson可以这样表达 Gson gson ...

  5. JSON数据解析(GSON方式) (转)

    JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,为Web应用开发提供了一种理想的数据交换格式. 在上一篇博文<Andro ...

  6. 使用Google 的 gson方式解析json

    gson支持解析的类型还是比较全面的,包括JavaBean,List<JavaBean>,List<String>,Map等,使用起来也是比较方便,下面根据代码示例给出总结: ...

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

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

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

  9. 在线聊天项目1.4版 使用Gson方法解析Json字符串以便重构request和response的各种请求和响应 解决聊天不畅问题 Gson包下载地址

    在线聊天项目结构图: 多用户登陆效果图: 多用户聊天效果图: 数据库效果图: 重新构建了Server类,使用了Gson方法,通过解析Json字符串,增加Info类,简化判断过程. Server类代码如 ...

随机推荐

  1. Ajax的text/plain、application/x-www-form-urlencoded和application/json

    Ajax的text/plain.application/x-www-form-urlencoded和application/json HTTP请求中,如果是get请求,那么表单参数以name=valu ...

  2. loadrunner 脚本中文乱码

    loadrunner 脚本中文乱码 1.新建脚本--->选择协议(Http)-->选项-->高级-->选择“支持字符集”并点选“UTF-8”: 2.在回放脚本之前:Vuser- ...

  3. Dubbo的容错与负载均衡

    虽然前面在介绍dubbo中写过这块内容,但是不够充分,这里详细写一下,在以后研究中,还会继续补充程序原理. 一:容错 1.机制 在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failove ...

  4. ubuntu16.04 更换源

    1.备份 sudo cp /etc/apt/source.list /etc/apt/source.list.bak 2.打开/etc/apt/source.list,并删除所有内容 sudo ged ...

  5. js包

    1.base.js /*语法: $("选择器") 工厂函数 */       /*寻找页面中name属性值是haha的元素*/   $("[name='haha']&qu ...

  6. H5 video标签视频加载存在的问题

    客户发现上传的视频无法播放,然后主管让我解决这个问题,这个页面不是我负责的,我看了代码,发现视频用的h5标签video标签加载视频.我看了没问题,然后 我先用ie浏览器打开,视频加载没问题.然后我给主 ...

  7. go语言解析 map[string]interface{} 数据格式

    原文:https://blog.csdn.net/Nick_666/article/details/79801914 map记得分配内存 解析出来的int类型会变成float64类型 注意判断不为ni ...

  8. 详解Python中的__init__和__new__(静态方法)

    一.__init__ 方法是什么? 使用Python写过面向对象的代码的同学,可能对 __init__ 方法已经非常熟悉了,__init__ 方法通常用在初始化一个类实例的时候.例如: #-*- co ...

  9. 学习Git操作的好资源

    网上资源很多,极大的方便了我们学习新东西. 今天找到了几个简单明了的Git教程,用以备录共享. Learn Git Branching  http://pcottle.github.io/learnG ...

  10. Problem A&B: 开宝箱 1/2 (最沙雕的做法)(未用指针做) 改:附上一种指针做法

    Description 急先锋是一个商人,有一天找到了一个宝箱,宝箱需要正确的密码才能打开.同时他发现宝箱上有一个数字,和一份密码表.密码表上有n个密码,只有一个密码是正确的. 急先锋所在的岛上有m个 ...