LoginSignup
19
16

More than 5 years have passed since last update.

Gson User Guide を和訳

Last updated at Posted at 2015-07-05

最終更新日:2015/07/12


間違いや和訳ミスがあれば随時ご指摘ください。
また、内容の間違いには一切の責任を持ちませんのでご了承ください。

Overview

Gson は Java Object を JSON へ変換するためのライブラリである
また JSON 文字列から Java Object への変換も可能である。
Gson は http://code.google.com/p/google-gson によって提供されるオープンソースプロジェクトである

Gson は任意の Java Object で利用可能で、ソースコード上にまだ存在しない Java Object であっても取り扱える。

Goals for Gson

  • toString() や Factory Method のコンストラクタのように簡単に利用し、Java Object と JSON の間でコンバートすることができる
  • 既存の変更不可なオブジェクトもコンバートすることができる
  • オブジェクトの表示方法を変更できる
  • 任意の複雑なオブジェクトをサポートしている
  • コンパクトかつ読みやすく生成された JSON を出力できる

Gson Preformance and Scalability

ここにデスクトップマシン(2コアCPU、8GB RAM、64bit Ubuntu)における様々なテスト結果がある

  • Strings:25MB を超える文字列のデシリアライズで問題が起きなかった(詳しくは PerformanceTest 内の disabled_testStringDeserializationPerformance を参照)
  • Large collections:
    • 1,400万の要素を持つオブジェクトのシリアライズ(詳しくは PerformanceTest 内の disabled_testLargeCollectionSerialization を参照)
    • 8万7千の要素を持つオブジェクトのデシリアライズ(詳しくは PerformanceTest 内の disabled_testLargeCollectionDeserialization を参照)
  • Gson 1.4 では配列およびコレクションの最大デシリアライズサイズが 80KB から 11MB 超まで増加された

Note:これらのテストを実行するためには disabled_ を外すこと。我々はこれを JUnitによる各テストを停止するために利用した。

Gson Users

Gson は Google 内部で作られ、現在ではいくつかのプロジェクトで利用されている。Gson は様々な企業のプロジェクトで利用されている。くわしくはこちら

Using Gson

Gson を利用するための基本的なクラスは new Gson() よ生成するだけである。
また、バージョン管理のように利用するための様々な設定値を利用してインスタンスを生成するための GsonBuilder も用意されている。

Gson インスタンスは Json 操作を行う間のどんな状態も保持しない。
そのため、複数の Json をシリアライズ/デシリアライズするために同じオブジェクトを再利用することができる。

(Serialization)

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

(Deserialization)

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() {
    // no-args constructor
  }
}

(Serialization)

BagOfPrimitives obj = new BagOfPrimitives();
Gson gson = new Gson();
String json = gson.toJson(obj);  
==> json is {"value1":1,"value2":"abc"}

なお、結果が永遠に再起されてしまうため、巡回的にシリアライズすることはできない

(Deserialization)

BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);
==> obj2 is just like obj

Finer Points with Objects

  • プライベートなフィールドとして利用できる(推奨)
  • フィールドがシリアライズもしくはデシリアライズを含むなどというアノテーション類は必要ない
  • フィールドが transient の場合、(デフォルトでは) JSON のシリアライズ/デシリアライズではそれは無視される
  • 実装では null となる
    • シリアライズ中、null 値は出力されない
    • デシリアライズ中、オブジェクト中でフィールドとして設定されていないものは JSON では null となる
  • 合成フィールドの場合、シリアライズ/デシリアライズでは無視され、JSON に含まれない
  • 内部クラス、匿名クラス、ローカルクラスのものと同様の外部クラスのフィールドはシリアライズ/デシリアライズでは無視される

Nested Classes (including inner Classes)

Gson はネストされたクラスも簡単にシリアライズできる

また、Gson はネストされたクラスのデシリアライズも可能である。
ただし、デシリアライズ時に利用可能なオブジェクトを引数にとるようなコンストラクタがないため、
純粋なインナークラスの自動的なデシリアライズは不可能である。

public class A {
  public String a;

  class B {
    public String b;

    public B() {
      // No args constructor for B
    }
  } 
}

NOTE: 上記のクラス B は(デフォルトでは) Gson にシリアライズ不可能である

クラス B がインナークラスのため、Gson が {"b":"abc"} をクラス B にはデシリアライズできない。
もしクラス B が static class 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();
  }
}

上記は動作するが、推奨はしない。

Array Examples

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

(Serialication)

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

(Deserialization)

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

複雑な要素で構成された多次元配列にも対応している。

Collections Examples

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 は ints と同じである

Fairly hideous: どのようにコレクションを定義するか記載しておく
不幸にも、これを Java ではうまく表現できない。

Collections Limitations

  • 任意のオブジェクトをシリアライズ可能であるが、任意のオブジェクトからのデシリアライズはできない
    • なぜなら、ユーザがオブジェクトの型を指定する方法がないためである
  • デシリアライズでは、コレクションは特定のジェネリクス型である必要がある

これらが意味するのは、良い Java を書いていればほとんど問題になることはないということである。

Serializing and Deserializing Generic Types

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); // May not serialize foo.value correctly

gson.fromJson(json, foo.getClass()); // Fails to deserialize foo.value as Bar

上記のコードでは Gson が型の情報を取得すために list.getClass を実行するため、Bar 型の解決に失敗してしまう。
そのためこのメソッドは Foo.class を返してしまう。
これが意味するのは、Gson はこのオブジェクトの方が Foo ではなく Foo であることを知る方法がないことである。

この問題を解決するためには、ジェネリクス型の正しい型情報を知らせてやる必要がある。
これは TypeToken クラスを利用することで実現できる

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

gson.fromJson(json, fooType);

この記法は型情報を返却する getType() を持つ匿名のインナークラス fooType の型取得するために使われる。

Serializing and Deserializing Collection with Objects of Arbitrary Types

['hello',5,{name:'GREETINGS',source:'guest'}] のような異なる型を持つ JSON 配列を扱う場合

これは同様に下記のコレクションのように表せられる

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) を利用することでシリアライズが可能である。

しかし、Gson が入力された型をどのようにマッピングするか把握する方法がないため、fromJson(json, Collection.class) を用いたデシリアライズは行えない。
そのため、Gson の fromJson では、コレクションの型を知らせてやる必要がある。
このために、3つのオプションが用意されている。

  • Option1: 配列の要素をパースするために、Gson のパース API (低レベルなひどい、DOM を利用した Json パーサ) を用いて、各要素で Gson.fromJson() を利用する。ここにその例がある。
  • Option2: 各配列の要素を決め、最適なオブジェクトにマッピングするための Collection クラスの型アダプタを用意する。この方法のデメリットは、Gson を用いて他の型をデシリアライズがめちゃくちゃになってしまうことがある。
  • fromJson と共に 適した型を持つ MyCollectionMemberType を用意し、Collection を利用する。この方法は、配列がトップレベルの要素の場合や保持している Collection の型を変更可能にするための唯一の方法となるだろう。

Built-in Serializers and Deserializers

Gson にはよく使われるようなクラスのシリアライザ/デシリアライザが用意されているが、それらの初期表示は不適切なものになるだろう。

ここに、そのようなクラスのリストを挙げる。

  1. java.net.URL to match it with strings like "http://code.google.com/p/google-gson/".
  2. java.net.URI to match it with strings like "/p/google-gson/".

JodaTime のようなよくあるクラスのソースコードはここで探すことができる。

Custom Serialization and Deserialization

デフォルト表示は時に必要とする者とは異なる。
これらは DateTime などのライブラリクラスでよく起きる。

そのため、Gson ではカスタムしたシリアライザ/デシリアライザを定義することができる。
これは下記に示す 2つのパートから行われる

  • シリアライザ:オブジェクトに対するカスタムシリアライズザを定義する必要がある
  • デシリアライザ:型に対するカスタムデシリアライザを定義する必要がある
  • インスタンスクリエイタ:引数のないコンストラクタもしくはデシリアライザが用意されている場合、特に必要はない
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());

型アダプタが1つ以上のインターフェースを実装し、それがすべてを定義じている場合、registerTypeAdapter はチェックされることになる。

Writing a Serializer

ここに JodaTime の DateTime クラスをシリアライズする際の例を示す。

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

Gson は DateTime オブジェクトをシリアライズする際に toJson() を呼び出す。

Writing a Deserializer

ここに 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());
  }
}

Gson は JSON 文字列を DateTime オブジェクトにデシリアライズする際に fromJson() を呼び出す必要がある。

Finer points with Serializers and Deserializers

すべてのジェネリクス型を元の型のように扱いたい場合、ハンドラを用意したい場合がある。

  • 例えば、Id の表示/変換のための Id クラスを持っているとする (つまり、内部向けおよび外部向けの表示形式である)
  • Id 型 はすべてのジェネリクス型に対するシリアライズザを持つ
    • 基本的に id の値を出力する
  • デシリアライザはとても似ているが、厳密には同じではない
    • Id 型のインスタンスを返すような new Id(Class<T>, String) を呼び出す必要がある。

Gson では、これに対するハンドラを登録することができる。
特殊ジェネリクス型への特殊なハンドラというものを登録することができるのである(Id<RequiresSpecialHandling> は特殊なハンドラを必要とするということである)

toJson および fromJson のための Type パラメータはすべての方を元の型に対応させるハンドラを記述するためのジェネリクス型に関する情報を含んでいる。

Writing an Instance Creator

オブジェクトをデシリアライズする際、Gson はクラスのデフォルトインスタンスを生成する必要がある。

シリアライズザやデシリアライザのための仕様に沿ったクラスは引数なしのコンストラクタを持っている必要がある。

  • public もしくは private に関係なく、である

通常、引数なしのコンストラクタが実装されていないライブラリクラスなどに対応するために、インスタンスクリエイタが必要となる。

Instance Creator Example

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

型はジェネリクス型である必要がある

  • 非常に多くの場合、コンストラクタを実行するためには特殊なジェネリクス型の情報が必要となる
  • 例えば、Id クラスが Id クラスを生成するクラスを保持しているときなどである

InstanceCreator for a Parameterized Type

インスタンス化しようとしている型がパラメータ化されていることがある。
大抵の場合、実際の方は元の型となるため、これは問題とはならない。
ここに例を挙げる。

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 メソッドに移動することができる。
ここに例を示す。

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 クラスのインスタンスは生成されない。
この問題は、type の消失したパラメータを用いて解決される。
この時、type オブジェクトは Id のパラメータ化された型であり、この時実際のインスタンスは Id となる必要がある。
Id クラスは 1つのパラメータ化された型 T を持つため、Foo クラスの持つ getActualTypeArgument() によって返される配列から、ゼロ番目の要素を利用する。

Compact Vs. Pretty Printing for JSON Output Format

Gson によって提供される デフォルトの JSON オブジェクトはコンパクトなフォーマットとなっている。
これは出力される JSON 構造の中に空白などがないことを示す。
そのため、出力される JSON にはどんな名前、値、オブジェクトフィールド、配列の間にもスペースが入っていない。
その上、null 値は出力から無視される(Note: ここで言う null 値にはコレクションや配列、オブジェクトも含まれる)
Gson を用いて null を出力する方法については、Null Object Supportを参照して欲しい。

Pretty Print 機能を利用したい場合は、Gson インスタンスを GsonBuilder を用いて生成する必要がある。
JsonFomatter は public な API ではないため、クライアントは JSON のデフォルト出力フォーマットを定義することはできない。
今のところ、デフォルトの JsonPrintFormatter は 1行あたり 80文字、2文字のインデント、4文字の右マージンとして定義されている。

下記に JsonCompactFormatter に代わって JsonPrintFormatter を利用する Gson インスタンスの構成方法を示す。

Gson gson = new GsonBuilder().setPrettyPrinting().create();
String jsonOutput = gson.toJson(someObject);

Null Object Support

Gson によって実装されるデフォルトの動作では、null オブジェクトは無視される。
これによって、出力はコンパクトとなるが、クライアントは JSON フォーマットを Java コードに再コンバートするために、これらフィールドの初期値を設定する必要がある

ここに null を出力する Gson インスタンスの生成方法を示す。

Gson gson = new GsonBuilder().serializeNulls().create();

NOTE: 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);
======== OUTPUT ========
{"s":null,"i":5}
null

Versioning Support

同じオブジェクトに対する複数のバージョン管理機能は @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);
======== OUTPUT ========
{"newField":"new","field":"old"}

{"newerField":"newer","newField":"new","field":"old"}

Excluding Fields From Serialization and Deserialization

Gson はトップレベルクラス、フォールド、そしてフィールドタイプを排除するための、非常に多くの機能をサポートしている。
以下に示すものは、フィールドやクラスを排除するためのプラグイン機能である。
以下の機能が求めるものを満たさない場合は、カスタムシリアライズザ/デシリアライザを利用できる

Java Modifier Exclusion

デフォルトの動作では、transient として指定されているフィールドは除外される。
また、"static" として指定されているものもデフォルトでは除外される。
もし transient なフィールドも含めたい場合は、下記のようにして設定できる。

import java.lang.reflect.Modifier;

Gson gson = new GsonBuilder()
    .excludeFieldsWithModifiers(Modifier.STATIC)
    .create();

NOTE: excludeFieldsWithModifiers の中で Modifier 定数はいくつでも設定可能である。

例:

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

Gson's @Expose

この機能は JSON のシリアライズ/デシリアライズを行うにあたり、いくつかのフィールドオブジェクトを除外すべきか考慮するための機能を提供する。
このアノテーションを使うためには、 new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create() として Gson を生成する必要がある。
生成された Gson オブジェクトは @Expose アノテーションによって指定されていない全てのフィールドを除外する。

User Defined Exclusion Strategies

もしこれまでに示した除外機能が正常に働かない場合は、自分自身で除外機構を作り、 Gson にプラグインさせることができる。
詳細は ExclusionStrategy の JavaDoc に記載がある。

下記の例は @Foo アノテーションによってクラス内のフィールドを除外する方法を示している。

@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);
}
======== OUTPUT ========
{"longField":1234}

JSON Field Naming Support

Gson はスタンダードな Java フィールドの命名 (例:"sampleFieldNameInJava" のようなキャメルケース) を Json の命名 (例:"sample_field_name_in_java") に変更するコンバート機構を幾つか有している。
予め用意されている命名規則については FieldNamingPolicy に情報が記載されている。

各フィールドに対して独自の命名規則を設定する方法として、アノテーションベースの方法がある。
ここで、アノテーションベースの方法では "Runtime" 例外を引き起こす可能せいがあることを知っておく必要がある。

下記に例を示す。

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);
======== OUTPUT ========
{"custom_naming":"first","SomeOtherField":"second"}

独自の命名規則をもたせる場合は @SirializedName アノテーションを用いて実現できる (こちらのやりとりも見て欲しい)

Sharing State Across Custom Serializers and Deserializers

時に、カスタムシリアライズザ/デシリアライザの間で状態を共有したい場合があるだろう(こちらの議論も参照)
これには下記に示す 3つの方法がある。

  1. static フィールドを用いて共有する状態を保持する。
  2. 共有する状態を保持するために、シリアライズザ/デシリアライザを親の型で宣言し、親のインスタンスを利用する。
  3. Java の ThreadLocal を利用する

1 および 2 はスレッドセーフではないが、3 はスレッドセーフである。

Streaming

さらなる Gson のオプションやデータバインディングを利用するために、stream を用いて Gson を読み書きすることができる。
それぞれの利点を利用するために、ストリーミングとオブジェクトモデルを組み合わせて利用することができる。

Issues in Designing Gson

Gson を作るにあたって直面している問題やそれに関する議論については Gson design document に記載がある。
ここには Json のコンバートに関する Gson とその他ライブラリの比較も記載されている。

Future Enhancements to Gson

新しい機能の提案一覧や新しいものを提案したい場合は、プロジェクトの下にある Issues section を参照して欲しい。

19
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
16