1概要

この短いチュートリアルでは、Javaクラスの1つ以上のフィールドとそのサブクラスをGsonシリアライゼーションから除外するために利用できるオプションについて説明します。


2初期設定

まずクラスを定義しましょう。

@Data
@AllArgsConstructor
public class MyClass {
    private long id;
    private String name;
    private String other;
    private MySubClass subclass;
}

@Data
@AllArgsConstructor
public class MySubClass {
    private long id;
    private String description;
    private String otherVerboseInfo;
}

便宜上、それらに

Lombok

と注釈を付けました(ゲッター、セッター、コンストラクターのための構文糖…​)

今それらを移入しましょう:

MySubClass subclass = new MySubClass(42L, "the answer", "Verbose field not to serialize")
MyClass source = new MyClass(1L, "foo", "bar", subclass);

私たちの目標は

MyClass.other



MySubClass.otherVerboseInfo

フィールドが直列化されないようにすることです。

期待される出力は次のとおりです。

{
  "id":1,
  "name":"foo",
  "subclass":{
    "id":42,
    "description":"the answer"
  }
}

Javaの場合:

String expectedResult = "{\"id\":1,\"name\":\"foo\",\"subclass\":{\"id\":42,\"description\":\"the answer\"}}";


3トランジェントモディファイア


transient

修飾子でフィールドをマークすることができます。

public class MyClass {
    private long id;
    private String name;
    private transient String other;
    private MySubClass subclass;
}

public class MySubClass {
    private long id;
    private String description;
    private transient String otherVerboseInfo;
}

Gsonシリアライザは、transientとして宣言されたすべてのフィールドを無視します。

String jsonString = new Gson().toJson(source);
assertEquals(expectedResult, jsonString);

これは非常に高速ですが、深刻なマイナス面もあります。

Gsonだけでなく

すべてのシリアライゼーションツールが一時的なものを考慮します** 。

トランジェントはシリアライゼーションから除外するJavaの方法です、そして私たちの分野はまた、

Serializable

のシリアライゼーションによって、そして私たちのオブジェクトを管理するすべてのライブラリツールまたはフレームワークによって除外されます。

さらに、

transient

キーワードは常にシリアライゼーションとデシリアライゼーションの両方で機能しますが、ユースケースによっては制限される場合があります。


4

@ Expose

注釈

どのフィールドを直列化するかを宣言し、他のフィールドを無視するためにそれを使用することができます。

public class MyClass {
    @Expose
    private long id;
    @Expose
    private String name;
    private String other;
    @Expose
    private MySubClass subclass;
}

public class MySubClass {
    @Expose
    private long id;
    @Expose
    private String description;
    private String otherVerboseInfo;
}

そのためには、GsonをGsonBuilderでインスタンス化する必要があります。

Gson gson = new GsonBuilder()
  .excludeFieldsWithoutExposeAnnotation()
  .create();
String jsonString = gson.toJson(source);
assertEquals(expectedResult, jsonString);

今回は、フィルタリングを直列化、非直列化、またはその両方に対して行うかどうかをフィールドレベルで制御できます(デフォルト)。


MyClass.other

がシリアライズされないようにする方法を見てみましょう。

@Expose(serialize = false, deserialize = true)
private String other;

これはGsonが提供する最も簡単な方法であり、他のライブラリには影響しませんが、コードの冗長性を意味する可能性があります。 100個のフィールドを持つクラスがあり、1つのフィールドだけを除外したい場合、99個のアノテーションを書く必要がありますが、これはやり過ぎです。


5

除外戦略


高度にカスタマイズ可能な解決策はhttps://google.github.io/gson/apidocs/com/google/gson/ExclusionStrategy.html[

com.google.gson.

ExclusionStrategy


]の使用です。

それは私たちがカスタムの基準でフィールド(そして/またはクラス)をシリアル化するかどうかGsonBuilderに指示するための戦略を(外部的にあるいはAnonimous Inner Classで)定義することを可能にします。

Gson gson = new GsonBuilder()
  .addSerializationExclusionStrategy(strategy)
  .create();
String jsonString = gson.toJson(source);

assertEquals(expectedResult, jsonString);

スマート戦略の使用例をいくつか見てみましょう。


5.1. クラスとフィールド名を使って

もちろん、1つ以上のフィールド/クラス名をハードコードすることもできます。

ExclusionStrategy strategy = new ExclusionStrategy() {
    @Override
    public boolean shouldSkipField(FieldAttributes field) {
        if (field.getDeclaringClass() == MyClass.class && field.getName().equals("other")) {
            return true;
        }
        if (field.getDeclaringClass() == MySubClass.class && field.getName().equals("otherVerboseInfo")) {
            return true;
        }
        return false;
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return false;
    }
};

これは速くて簡単なことですが、あまり再利用できるわけではなく、属性の名前を変更した場合にエラーが発生する可能性もあります。


5.2. ビジネス基準とは

単純にブール値を返さなければならないので、そのメソッド内で好きなビジネスロジックをすべて実装できます。

次の例では、「other」で始まるすべてのフィールドを、それらが属するクラスに関係なく、直列化する必要がないフィールドとして識別します。

ExclusionStrategy strategy = new ExclusionStrategy() {
    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return false;
    }

    @Override
    public boolean shouldSkipField(FieldAttributes field) {
        return field.getName().startsWith("other");
    }
};


5.3. カスタム注釈付き

もう1つの賢い方法は、カスタム注釈を作成することです。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Exclude {}

それから

Exexpose

アノテーションと全く同じように動作させるために

ExclusionStrategy

を利用することができますが、その逆になります。

public class MyClass {
    private long id;
    private String name;
    @Exclude
    private String other;
    private MySubClass subclass;
}

public class MySubClass {
    private long id;
    private String description;
    @Exclude
    private String otherVerboseInfo;
}

そして、これが戦略です。

ExclusionStrategy strategy = new ExclusionStrategy() {
    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return false;
    }

    @Override
    public boolean shouldSkipField(FieldAttributes field) {
        return field.getAnnotation(Exclude.class) != null;
    }
};


このStackOverflowの回答

は最初にこのテクニックを説明しました。

それは私たちが一度アノテーションとストラテジーを書くことを可能にし、そして更なる修正なしに私達のフィールドに動的にアノテーションを付けることを可能にします。


5.4. 除外戦略をデシリアライゼーションに拡張

どの戦略を使用しても、いつ適用するかをいつでも制御できます。

シリアル化中のみ

Gson gson = new GsonBuilder().addSerializationExclusionStrategy(strategy)

逆シリアル化中のみ

Gson gson = new GsonBuilder().addDeserializationExclusionStrategy(strategy)

常に:

Gson gson = new GsonBuilder().setExclusionStrategies(strategy);


6. 結論

Gsonのシリアル化中にクラスとそのサブクラスからフィールドを除外するさまざまな方法を見ました。

また、すべてのソリューションの主な利点と落とし穴についても調査しました。

いつものように、完全なソースコードはhttps://github.com/eugenp/tutorials/tree/master/gson[over Github]から入手できます。