Gson 用戶指南 翻譯自Gson User Guide

原文:github.com/google/gson…html

概況

Gson是一個Java庫,它能夠用來把Java對象轉換爲JSON表達式,也能夠反過來把JSON字符串轉換成與之相同的Java對象 Gson能夠對任何Java對象使用包括那些你沒有源碼但已存在的對象java

Gson的目標

  • 提供簡單易用的機制相似於toString()和構造器(工廠模式)用來進行Java和JSON互相轉換
  • 容許把預先存在但沒法修改的對象轉換爲JSON或從JSON轉換
  • 容許對象的自定義表示
  • 支持任何複雜的對象
  • 生成緊湊易讀的JSON輸出

Gson的性能和可延展性

這裏提供的一些參數是從咱們的筆記本(雙核AMD皓龍處理器,8GB RAM,64位Ubuntu系統)上運行了不少的測試用例中獲取的,你也能夠用PerformanceTest類來從新運行這些測試案例git

  • 對於字符串:反序列化超過25MB的字符串沒有任何問題(能夠查看PerformanceTest下的方法disabled_testStringDeserializationPerformance)github

  • 對於大型集合:json

    • 序列化過一個擁有1400萬個對象的集合(能夠查看PerformanceTest下的disabled_testLargeCollectionSerialization方法) -反序列化過一個擁有8.7萬個對象的集合(能夠查看PerformanceTest下的disabled_testLargeCollectionDeserialization方法)
  • Gson的1.4版本將字節數組和集合的反序列化最大值從80KB提升到了11MBapi

提示:運行這些測試用例時要刪除disabled_前綴,咱們使用這些前綴是爲了在每次運行JUnit測試時避免運行到上述例子數組

Gson的用戶

Gson本來是創造給Google內部人員使用並運用在目前的的不少Google項目中。如今被不少公共項目和公司所使用安全

使用Gson

Gson的主要用到的類是Gson,你能夠直接經過調用new Gson()來生成,也能夠用類GsonBuilder來建立Gson實例進行自主的進行相似於版本控制的參數設置bash

Gson實例不會保留任何狀態當你調用Json操做時,因此你能夠隨意的重用一個對象操做多個Json序列化和反序列化操做maven

Android:在Gradle上使用Gson

dependencies {
    implementation 'com.google.code.gson:gson:2.8.5'
}
複製代碼

在Maven上使用Gson

要在Maven2/3中使用Gson,你能夠在Maven庫中找到Gson合適的版本添加到下面的dependency

<dependencies>
    <!--  Gson: Java to Json conversion -->
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.8.5</version>
      <scope>compile</scope>
    </dependency>
</dependencies>
複製代碼

這樣你的maven項目就能夠用Gson了

基本例子

// 序列化
Gson gson = new Gson();
gson.toJson(1);            // ==> 1
gson.toJson("abcd");       // ==> "abcd"
gson.toJson(new Long(10)); // ==> 10
int[] values = { 1 };
gson.toJson(values);       // ==> [1]

// 反序列化
int one = gson.fromJson("1", int.class);
Integer one = gson.fromJson("1", Integer.class);
Long one = gson.fromJson("1", Long.class);
Boolean false = gson.fromJson("false", Boolean.class);
String str = gson.fromJson("\"abc\"", String.class);
String[] anotherStr = gson.fromJson("[\"abc\"]", String[].class);
複製代碼

關於對象的例子

class BagOfPrimitives {
  private int value1 = 1;
  private String value2 = "abc";
  private transient int value3 = 3;
  BagOfPrimitives() {
    // 無參的構造方法
  }
}

// 序列化
BagOfPrimitives obj = new BagOfPrimitives();
Gson gson = new Gson();
String json = gson.toJson(obj);  

// ==> json is {"value1":1,"value2":"abc"}
複製代碼

注意你不能序列化有循環調用的對象,否則會返回一個無限遞歸的結果

// 反序列化
BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);
// ==> obj2 is just like obj
複製代碼

對象的細節

  • 對象裏使用private是徹底能夠運行的,並且咱們推薦你這麼作
  • 不須要聲明什麼區域須要被序列化或者反序列化,當前類的全部區域(包括全部的超類)都被默認包括進去
  • 若是字段被標記爲瞬態,(默認狀況下)會被忽略不會被包括在JSON序列化或者反序列化中
  • 這實現這麼處理的Null值
    • 序列化時,輸出中省略了Null字段。
    • 當反序列化時,當類中的區域在JSON結果中找不到時默認會設置:對象會設置爲null,數字類型會設置爲0,布爾類型會設置爲false
  • 若是類型是syntactic,會被忽略,不被包含在JSON序列化或反序列化
  • 與內部類,匿名類和本地類中的外部類對應的字段將被忽略,而且不包括在序列化或反序列化中。

嵌套類(包含內部類)

Gson能夠很容易的序列化靜態嵌套類

Gson 還能夠反序列化靜態嵌套類。可是,Gson 不能自動反序列化純內部類,由於它們的 無參構造函數也須要引用包含的可是還沒被反序列化的對象。您能夠經過使內部類變爲靜態內部類或爲其提供自定義 InstanceCreator 來解決此問題 這是一個例子:

public class A { 
  public String a; 

  class B { 

    public String b; 

    public B() {
      // B的無參構造函數
    }
  } 
}
複製代碼

注意:上述 B 類不能(默認狀況下)使用 Gson 序列化。

{"b":"abc"}因爲 B 類是一個內部類,Gson 不能反序列化爲 B 的實例。若是它被定義爲靜態類 B,那麼 Gson 就可以反序列化字符串。另外一種解決方案是爲 B 編寫自定義實例建立器。

public class InstanceCreatorForB implements InstanceCreator<A.B> {
  private final A a;
  public InstanceCreatorForB(A a)  {
    this.a = a;
  }
  public A.B createInstance(Type type) {
    return a.new B();
  }
}
複製代碼

上述代碼能夠運行可是不推薦

關於數組的例子

Gson gson = new Gson();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"abc", "def", "ghi"};

// Serialization
gson.toJson(ints);     // ==> [1,2,3,4,5]
gson.toJson(strings);  // ==> ["abc", "def", "ghi"]

// Deserialization
int[] ints2 = gson.fromJson("[1,2,3,4,5]", int[].class); 
// ==> ints2 will be same as ints
複製代碼

咱們也支持多尺寸數組和任何複雜的元素類型

集合例子

Gson gson = new Gson();
Collection<Integer> ints = Lists.immutableList(1,2,3,4,5);

// Serialization
String json = gson.toJson(ints);  // ==> json is [1,2,3,4,5]

// Deserialization
Type collectionType = new TypeToken<Collection<Integer>>(){}.getType();
Collection<Integer> ints2 = gson.fromJson(json, collectionType);
// ==> ints2 is same as int
複製代碼

可怕的現象:注意到咱們怎麼定義collection類型的嗎?不幸的是,在Java裏不能這麼作

集合的限制: Gson能夠序列化任意對象的集合,但不能從中反序列化,由於用戶沒法知道結果對象的類型。相反,在反序列化時,集合必須是特定的泛型類型。你們都知道,當遵循良好的編碼習慣就不多出現問題

當序列化和反序列化泛型

當你調用toJson(obj),Gson會調用obj.getClass()來獲取區域的信息去序列化,一樣,你能夠經過MyClass.class對象的fromJson(json, MyClass.class)方法.若是對象裏沒有泛型這麼作也能夠。可是,若是你的對象裏有泛型,這麼作泛型會丟失,由於Java的類型擦除,這有個例子來講明這點

class Foo<T> {
  T value;
}
Gson gson = new Gson();
Foo<Bar> foo = new Foo<Bar>();
gson.toJson(foo); // 這樣可能沒法正確的序列化 foo.value 

gson.fromJson(json, foo.getClass()); // 沒法把 foo.value 做爲 Bar 反序列化
複製代碼

上述代碼想賦予value的類型Bar但失敗了由於Gson調用了list.getClass()方法去獲取這個類的信息,可是返回一個純類Foo.class。這就是說Gson沒有方法知道你的對象的類型是Foo<Bar>而不是Foo

你能夠經過肯定正確的參數來代替泛型能夠解決上述問題,你也能夠用TypeToken

Type fooType = new TypeToken<Foo<Bar>>() {}.getType();
gson.toJson(foo, fooType);

gson.fromJson(json, fooType);
複製代碼

慣用方法是獲取fooType來實際定義一個匿名內部類來包含用來返回整個參數類型的getType()方法

序列化和反序列化帶有任意類型對象的集合

有時候你須要處理帶有雜亂類型的JSON數組,像是['hello',5,{name:'GREETINGS',source:'guest'}] 與之相等的Collection包含:

Collection collection = new ArrayList();
collection.add("hello");
collection.add(5);
collection.add(new Event("GREETINGS", "guest"));
複製代碼

Event定義成:

class Event {
  private String name;
  private String source;
  private Event(String name, String source) {
    this.name = name;
    this.source = source;
  }
}
複製代碼

你能夠直接用Gson去序列化集合並且不用作其餘任何事情,toJson(collection)會寫出你期待的輸出

可是,反序列化fromJson(json, Collection.class)沒法工做,由於 Gson 沒法知道如何將輸入映射到類型。Gson 要求您提供集合類型的通用版本fromJson()。因此,你有三個選擇:

  1. 使用 Gson 的解析器 API(低級流解析器或 DOM 解析器 JsonParser)來解析數組元素,而後Gson.fromJson()在每一個數組元素上使用。這是首選方法。這是一個演示如何執行此操做的示例

  2. 註冊一個類型適配器Collection.class,查看每一個數組成員並將它們映射到適當的對象。這種方法的缺點是它會搞砸 Gson 中其餘集合類型的反序列化。

  3. 註冊一個類型的適配器MyCollectionMemberType,並使用fromJson()Collection<MyCollectionMemberType>

僅當數組顯示爲頂級元素或者您能夠更改將集合保持爲類型的字段類型時,此方法纔可用Collection<MyCollectionMemberType>

內置的序列化器和反序列化器

Gson 有經常使用類的內置序列化器和反序列化器,其默認表示可能不合適。如下是此類的列表:

  1. java.net.URL 把它與字符串匹配 "https://github.com/google/gson/"
  2. java.net.URI 把它與字符串匹配 "/google/gson/"

您還能夠在此頁面找到一些經常使用類的源代碼,例如 JodaTime 。

自定義序列化和反序列化

有時默認表示不是您想要的。處理庫類(DateTime 等)時常常會出現這種狀況。Gson 容許您註冊本身的自定義序列化程序和反序列化程序。這是經過定義兩部分來完成的:

  • Json Serializers:須要爲對象定義自定義序列化

  • Json Deserializers:須要爲類型定義自定義反序列化

  • Instance Creators:若是無參構造函數可用或註冊了反序列化器,則不須要

GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(MyType2.class, new MyTypeAdapter());
gson.registerTypeAdapter(MyType.class, new MySerializer());
gson.registerTypeAdapter(MyType.class, new MyDeserializer());
gson.registerTypeAdapter(MyType.class, new MyInstanceCreator());
複製代碼

registerTypeAdapter 調用檢查類型適配器是否實現了多個這些接口併爲全部接口註冊它。

編寫Json Serializers

如下是如何爲 JodaTime DateTime類編寫自定義序列化程序的示例。

private class DateTimeSerializer implements JsonSerializer<DateTime> {
  public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {
    return new JsonPrimitive(src.toString());
  }
}
複製代碼

Gson在序列化過程當中遇到DateTime對象時調用serialize()

編寫Json Deserializers

下面是如何爲JodaTime DateTime類編寫自定義反序列化器的示例。

private class DateTimeDeserializer implements JsonDeserializer<DateTime> {
  public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
      throws JsonParseException {
    return new DateTime(json.getAsJsonPrimitive().getAsString());
  }
}
複製代碼

當須要將JSON字符串片斷反序列化爲DateTime對象時,Gson調用deserialize

序列化器和反序列化器的細節

一般,您但願爲與原始類型對應的全部泛型類型註冊單個處理程序

  • 例如,假設您有一個Id用於 id 表示 / 轉換的類(即內部表示與外部表示)。
  • Id<T> 對全部泛型類型具備相同序列化的類型
    • 基本上寫出 id 值
  • 反序列化很是類似但不徹底相同
    • 須要調用new Id(Class<T>, String)哪一個返回一個實例Id<T>

Gson 支持爲此註冊一個處理程序。您還能夠爲特定的泛型類型註冊特定的處理程序(好比Id<RequiresSpecialHandling>須要特殊處理)。在Type該參數toJson()fromJson()包含的通用類型的信息來幫助你編寫對應於同一原始類型的全部泛型類型單一的處理程序。

編寫Instance Creators

在反序列化 Object 時,Gson 須要建立該類的默認實例。用於序列化和反序列化的良好的類應該具備無參數構造函數。

  • 與用public仍是private無關

一般,在處理未定義無參數構造函數的庫類時,須要實例建立器

Instance Creators示例

private class MoneyInstanceCreator implements InstanceCreator<Money> {
  public Money createInstance(Type type) {
    return new Money("1000000", CurrencyCode.USD);
  }
}
複製代碼

類型能夠是相應的泛型類型

  • 對於調用須要特定泛型類型信息的構造函數很是有用
  • 例如,若是Id類存儲了正在爲其建立 Id 的類

參數化類型的 InstanceCreator

有時,您嘗試實例化的類型是參數化類型。一般,這不是問題,由於實際的實例是原始類型。這是一個例子:

class MyList<T> extends ArrayList<T> {
}

class MyListInstanceCreator implements InstanceCreator<MyList<?>> {
    @SuppressWarnings("unchecked")
  public MyList<?> createInstance(Type type) {
    // No need to use a parameterized list since the actual instance will have the raw type anyway.
    return new MyList();
  }
}
複製代碼

可是,有時您須要根據實際參數化類型建立實例。在這種狀況下,您可使用傳遞給createInstance方法的 type 參數。這是一個例子:

public class Id<T> {
  private final Class<T> classOfId;
  private final long value;
  public Id(Class<T> classOfId, long value) {
    this.classOfId = classOfId;
    this.value = value;
  }
}

class IdInstanceCreator implements InstanceCreator<Id<?>> {
  public Id<?> createInstance(Type type) {
    Type[] typeParameters = ((ParameterizedType)type).getActualTypeArguments();
    Type idType = typeParameters[0]; // Id has only one parameterized type T
    return Id.get((Class)idType, 0L);
  }
}
複製代碼

在上面的示例中,若是沒有實際傳入參數化類型的實際類型,則沒法建立 Id 類的實例。咱們經過使用傳遞的方法參數來解決這個問題typetype在這種狀況下,對象是 Java 參數化類型表示Id<Foo>實際實例應綁定到的位置Id<Foo>。因爲Idclass 只有一個參數化類型參數,T咱們使用返回的類型數組的第 0 個元素,在這種狀況下getActualTypeArgument()它將保存Foo.class

JSON輸出形式:緊湊輸出VS優雅輸出

Gson 提供的默認 JSON 輸出是緊湊的 JSON 格式。這意味着輸出 JSON 結構中不會有任何空格。所以,JSON 輸出中的字段名稱及其值,對象字段和數組內的對象之間不會有空格。一樣,輸出中將忽略 「null」 字段(注意:null 值仍將包含在對象的集合 / 數組中)。有關配置 Gson 以輸出全部空值的信息,請參閱Null 對象支持部分。

若是要使用 「優雅輸出」 功能,則必須使用GsonBuilder來建立Gson實例。JsonFormatter沒有在咱們的公共 API 公開,因此用戶沒法爲JSON的輸出形式作配置和調整,目前,咱們只提供JsonPrintFormatter默認行長度爲 80 個字符,2 個字符縮進和 4 個字符右邊距。

如下是一個示例,說明如何配置Gson實例以使用默認值JsonPrintFormatter而不是JsonCompactFormatter

Gson gson = new GsonBuilder().setPrettyPrinting().create();
String jsonOutput = gson.toJson(someObject);
複製代碼

空對象支持 在 Gson 中實現的默認行爲null是忽略對象字段。這容許更緊湊的輸出格式;可是,用戶必須爲這些字段定義默認值,由於 JSON 格式將轉換回其 Java 表單。

如下是配置Gson實例輸出 null 的方法:

Gson gson = new GsonBuilder().serializeNulls().create();
複製代碼

注意:在使用 Gson 序列化 null 時,它會向JsonElement結構添加JsonNull元素。所以,此對象可用於自定義序列化 / 反序列化。

這是一個例子:

public class Foo {
  private final String s;
  private final int i;

  public Foo() {
    this(null, 5);
  }

  public Foo(String s, int i) {
    this.s = s;
    this.i = i;
  }
}

Gson gson = new GsonBuilder().serializeNulls().create();
Foo foo = new Foo();
String json = gson.toJson(foo);
System.out.println(json);

json = gson.toJson(null);
System.out.println(json);
複製代碼

輸出是:

{"s":null,"i":5}
null
複製代碼

版本控制支持

使用@Since註釋能夠維護同一對象的多個版本。此批註可用於類,字段以及未來的發行版中的方法。要利用此功能,必須將Gson實例配置爲忽略任何大於某個版本號的字段 / 對象。若是沒有在Gson實例上設置任何版本,則不管版本如何,它都將序列化和反序列化全部字段和類。

public class VersionedClass {
  @Since(1.1) private final String newerField;
  @Since(1.0) private final String newField;
  private final String field;

  public VersionedClass() {
    this.newerField = "newer";
    this.newField = "new";
    this.field = "old";
  }
}

VersionedClass versionedObject = new VersionedClass();
Gson gson = new GsonBuilder().setVersion(1.0).create();
String jsonOutput = gson.toJson(someObject);
System.out.println(jsonOutput);
System.out.println();

gson = new Gson();
jsonOutput = gson.toJson(someObject);
System.out.println(jsonOutput);
複製代碼

輸出是:

{"newField":"new","field":"old"}
複製代碼

從序列化和反序列化中排除字段

Gson 支持許多排除頂級類,字段和字段類型的機制。下面列出的是可插入的機制,容許字段和類排除。若是如下機制都不能知足您的需求,那麼您始終可使用自定義序列化程序和反序列化程序

Java 修飾符排除

默認狀況下,若是將字段標記爲transient,則將排除該字段。一樣,若是某個字段被標記爲static默認狀況下將被排除。若是要包含一些瞬態字段,則能夠執行如下操做:

import java.lang.reflect.Modifier;
Gson gson = new GsonBuilder()
    .excludeFieldsWithModifiers(Modifier.STATIC)
    .create();
複製代碼

注意:您能夠爲方法excludeFieldsWithModifiers提供任意數量的Modifier常量。例如:

Gson gson = new GsonBuilder()
    .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE)
    .create();
複製代碼

GSON 的 @Expose

此功能提供了一種方法,您能夠將要排除的對象的某些字段標記爲序列化和反序列化爲 JSON。要使用此批註,必須使用建立 Gson new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()。建立的 Gson 實例將排除類中未標註@Expose註釋的全部字段。

用戶定義的排除策略

若是排除字段和類類型的上述機制對您不起做用,那麼您始終能夠編寫本身的排除策略並將其插入 Gson。有關ExclusionStrategy更多信息,請參閱 JavaDoc。

如下示例顯示如何排除標記有特定@Foo註釋的字段,並排除類的頂級類型(或聲明的字段類型)String

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Foo {
  // Field tag only annotation
}

public class SampleObjectForTest {
  @Foo private final int annotatedField;
  private final String stringField;
  private final long longField;
  private final Class<?> clazzField;

  public SampleObjectForTest() {
    annotatedField = 5;
    stringField = "someDefaultValue";
    longField = 1234;
  }
}

public class MyExclusionStrategy implements ExclusionStrategy {
  private final Class<?> typeToSkip;

  private MyExclusionStrategy(Class<?> typeToSkip) {
    this.typeToSkip = typeToSkip;
  }

  public boolean shouldSkipClass(Class<?> clazz) {
    return (clazz == typeToSkip);
  }

  public boolean shouldSkipField(FieldAttributes f) {
    return f.getAnnotation(Foo.class) != null;
  }
}

public static void main(String[] args) {
  Gson gson = new GsonBuilder()
      .setExclusionStrategies(new MyExclusionStrategy(String.class))
      .serializeNulls()
      .create();
  SampleObjectForTest src = new SampleObjectForTest();
  String json = gson.toJson(src);
  System.out.println(json);
}
複製代碼

輸出是:

{"longField":1234}
複製代碼

JSON 字段命名支持

Gson 支持一些預約義的字段命名策略,以將標準 Java 字段名稱(即以小寫字母 --- 開頭的駝峯名稱sampleFieldNameInJava)轉換爲 Json 字段名稱(即sample_field_name_in_javaSampleFieldNameInJava)。有關預約義命名策略的信息,請參閱FieldNamingPolicy類。

它還具備基於註釋的策略,容許用戶基於每一個字段定義自定義名稱。請注意,基於註釋的策略具備字段名稱驗證,若是提供了無效的字段名稱做爲註釋值,則會引起 「運行時」 異常。

如下是如何使用兩個 Gson 命名策略功能的示例:

private class SomeObject {
  @SerializedName("custom_naming") private final String someField;
  private final String someOtherField;

  public SomeObject(String a, String b) {
    this.someField = a;
    this.someOtherField = b;
  }
}

SomeObject someObject = new SomeObject("first", "second");
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
String jsonRepresentation = gson.toJson(someObject);
System.out.println(jsonRepresentation);
複製代碼

輸出是:

{"custom_naming":"first","SomeOtherField":"second"}
複製代碼

若是您須要自定義命名策略(請參閱此討論),可使用@SerializedName的註解。

在自定義序列化器和反序列化器之間共享狀態

有時您須要在自定義序列化器 / 反序列化器之間共享狀態(請參閱此討論)。您可使用如下三種策略來完成此任務:

  1. 在靜態字段中存儲共享狀態
  2. 將序列化器 / 反序列化器聲明爲父類型的內部類,並使用父類型的實例字段來存儲共享狀態
  3. 使用 Java ThreadLocal

1 和 2 不是線程安全選項,但 3 是。

除了 Gson 的對象模型和數據綁定以外,您還可使用 Gson 讀取和寫入。您還能夠組合流和對象模型訪問,以得到兩種方法中的最佳方法。

在設計 Gson 時遇到的問題

有關咱們在設計 Gson 時遇到的問題的討論,請參閱Gson 設計文檔。它還包括 Gson 與可用於 Json 轉換的其餘 Java 庫的比較。

Gson 將來的改進

有關最新的更新計劃或者若是你有新的建議,請參閱項目網站下的 Issues。部分

相關文章
相關標籤/搜索