web-dev-qa-db-ja.com

Gsonがnullまたは空でない場合にのみフィールドをシリアル化する

Javaオブジェクトをjsonに変換する必要があるという要件があります。

私はそのためにGsonを使用していますが、null以外の値または空でない値のみをシリアル化するコンバーターが必要です。

例えば:

//my Java object looks like
class TestObject{
    String test1;
    String test2;
    OtherObject otherObject = new OtherObject();
}

今、このオブジェクトをjsonに変換する私のGsonインスタンスは次のようになります

Gson gson = new Gson();
TestObject obj = new TestObject();
obj.test1 = "test1";
obj.test2 = "";

String jsonStr = gson.toJson(obj);
println jsonStr;

上記の印刷では、結果は

{"test1":"test1", "test2":"", "otherObject":{}}

ここでは、結果が欲しいだけです

{"test1":"test1"}

Test2は空で、otherObjectは空なので、jsonデータにシリアル化されたくありません。

ところで、私はGroovy/Grailsを使用しているので、これに適したプラグインがあれば、gsonシリアル化クラスをカスタマイズするための提案がなければ良いでしょう。

28
zdesam

独自のTypeAdapterを作成します

public class MyTypeAdapter extends TypeAdapter<TestObject>() {

    @Override
    public void write(JsonWriter out, TestObject value) throws IOException {
        out.beginObject();
        if (!Strings.isNullOrEmpty(value.test1)) {
            out.name("test1");
            out.value(value.test1);
        }

        if (!Strings.isNullOrEmpty(value.test2)) {
            out.name("test2");
            out.value(value.test1);
        }
        /* similar check for otherObject */         
        out.endObject();    
    }

    @Override
    public TestObject read(JsonReader in) throws IOException {
        // do something similar, but the other way around
    }
}

その後、Gsonで登録できます。

Gson gson = new GsonBuilder().registerTypeAdapter(TestObject.class, new MyTypeAdapter()).create();
TestObject obj = new TestObject();
obj.test1 = "test1";
obj.test2 = "";
System.out.println(gson.toJson(obj));

生産する

 {"test1":"test1"}

GsonBuilderクラスには、独自のシリアライゼーション/デシリアライゼーション戦略を作成し、タイプアダプタを登録し、他のパラメータを設定するための多数のメソッドがあります。

StringsはGuavaクラスです。その依存関係が必要ない場合は、独自のチェックを行うことができます。

21

私がanswerを使用してTypeAdapterで個人的に嫌いなのは、クラス全体のすべてのフィールドを記述する必要があるという事実です。これは、50個のフィールド(ifの50個のTypeAdapterブロックを意味します).
私のソリューションはReflectionに基づいており、デフォルトではGsonはnull値フィールドをシリアル化しません。
DocumentModelというドキュメントを作成するAPIのデータを保持する特別なクラスがあります。これには約50のフィールドがあり、Stringフィールドに ""(空ではないが空ではない)値または空の配列を送信しませんサーバ。そこで、すべての空のフィールドを無効にしてオブジェクトのコピーを返す特別なメソッドを作成しました。注-デフォルトでは、DocumentModelインスタンスのすべての配列は空の(長さゼロの)配列として初期化されているため、nullになることはありません。

public DocumentModel getSerializableCopy() {
    Field fields[] = new Field[]{};
    try {
        // returns the array of Field objects representing the public fields
        fields = DocumentModel.class.getDeclaredFields();
    } catch (Exception e) {
        e.printStackTrace();
    }
    DocumentModel copy = new DocumentModel();
    Object value;
    for (Field field : fields) {
        try {
            value = field.get(this);
            if (value instanceof String && TextUtils.isEmpty((String) value)) {
                field.set(copy, null);
            // note: here array is not being checked for null!
            else if (value instanceof Object[] && ((Object[]) value).length == 0) {
                field.set(copy, null);
            } else
                field.set(copy, value);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    return copy;
}

このメソッドを使用して、このメソッドの作成後にフィールドが追加または削除されたかどうかは気にしません。残っている唯一の問題は、Stringまたは配列ではないカスタム型フィールドをチェックすることですが、これは特定のクラスに依存し、if/elseブロックで追加コード化する必要があります。

7
Stan

私には問題はgsonにはないようです。 Gsonは、nullと空の文字列の違いを正しく追跡します。その区別を消去してもよろしいですか? TestObjectを使用するすべてのクラスは気にしませんか?

違いを気にしない場合にできることは、シリアル化する前にTestObject内で空の文字列をnullに変更することです。または、空の文字列がnullに設定されるようにTestObjectでセッターを作成してください。そのようにして、クラス内で空の文字列がnullと同じであると厳密に定義します。セッターの外部で値を設定できないようにする必要があります。

3
toto2