Gson

前言

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

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

 

引入配置

implementation 'com.google.code.gson:gson:2.8.5'

 

基本概念

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

Gson解决的问题

  • 提供一种像toString()和构造方法的很简单的机制,来实现Java 对象和Json之间的互相转换。
  • 允许已经存在的无法改变的对象,转换成Json,或者Json转换成已存在的对象。
  • 允许自定义对象的表现形式
  • 支持任意的复杂对象
  • 能够生成可压缩和可读的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);

由于shirtNumber和country作用版本分别是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-10和isbn-13, 我们怎么把字段数据isbn10和isbn13转化为JSON数据需要的isbn-10和isbn-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 注解使用了一个value, alternate字段,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);

    }

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值