web-dev-qa-db-ja.com

シリアル化中にGsonがフィールドを追加する

Gsonでのシリアル化中にカスタムフィールドを追加する簡単な方法が見つからず、他の誰かが助けてくれることを望んでいました。

これが私の問題を示すサンプルクラスです:

public class A {
  String id;
  String name;
  ...
}

クラスAをシリアル化すると、次のようなものが返されます。

{ "id":"123", "name":"John Doe", "url_to_user":"http://www.example.com/123" }

ここで、url_to_userはクラスAのインスタンスに格納されていませんが、クラスAのインスタンスのデータで生成できます。

これを行う簡単な方法はありますか? 1つのフィールドを追加するためだけにシリアライザ全体を作成することは避けたいと思います。

28
joncalhoun

使用する Gson.toJsonTreeは、動的に対話できるJsonElementを取得します。

A a = getYourAInstanceHere();
Gson gson = new Gson();
JsonElement jsonElement = gson.toJsonTree(a);
jsonElement.getAsJsonObject().addProperty("url_to_user", url);
return gson.toJson(jsonElement);
51
Jeff Bowman

まあ、あなたが多くの時間を欠いているとき、最高評価の答えはかなり速いものであり、本質的に悪いではありませんが、ここに問題があります:適切な 懸念の分離がありません

ビジネスロジックを記述しているのと同じ場所で、シリアル化されたJSONを変更します。 TypeAdapterまたはJsonSerializer内ですべてのシリアル化を行う必要があります。

懸念の適切な分離をどのように維持できますか?

答えは少し複雑さを取り巻くものですが、アーキテクチャはそれを要求します。ここに行きます(私の- その他の答え から取得):

まず、型にカスタムシリアライザーを使用します。次に、基本クラスとラッパーサブクラス内にcopyコンストラクターを次のように作成する必要があります。

注:カスタムシリアライザーはやり過ぎのように見えるかもしれませんが、私を信用してください。保守性のために長期的に見れば見返りがあります。

// Lets say the base class is named Cat
public class Cat {

    public String name;

    public Cat(String name) {
        super();
        this.name = name;
    }
    // COPY CONSTRUCTOR
    public Cat(Cat cat) {
        this.name = cat.name;
    }

    @Override
    public String sound() {
        return name + " : \"meaow\"";
    };
}



    // The wrapper subclass for serialization
public class CatWrapper extends Cat{


    public CatWrapper(String name) {
        super(name);
    }

    public CatWrapper(Cat cat) {
        super(cat);
    }
}

そして、タイプCatのシリアライザ:

public class CatSerializer implements JsonSerializer<Cat> {

    @Override
    public JsonElement serialize(Cat src, Type typeOfSrc, JsonSerializationContext context) {

        // Essentially the same as the type Cat
        JsonElement catWrapped = context.serialize(new CatWrapper(src));

        // Here, we can customize the generated JSON from the wrapper as we want.
        // We can add a field, remove a field, etc.

        // The main logic from the top rated answer now here instead of *spilling* around(Kindly ignore the cat having a url for the sake of example)
        return catWrapped.getAsJsonObject().addProperty("url_to_user", url);
    }
}

では、なぜコピーコンストラクタなのでしょうか。

まあ、コピーコンストラクターを定義すると、基本クラスがどれだけ変化しても、ラッパーは同じ役割を継続します。第2に、コピーコンストラクタを定義せず、基本クラスを単にサブクラス化する場合、拡張クラス、つまりCatWrapperに関して「話す」必要があります。コンポーネントがラッパー型ではなく、基本クラスの観点から話す可能性は十分にあります。

0
pulp_fiction