Подтвердить что ты не робот

Поле добавления Gson во время сериализации

Я не могу найти простой способ добавить настраиваемое поле во время сериализации в Gson, и я надеялся, что кто-то может помочь.

Вот пример класса, чтобы показать мою проблему:

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

Когда я сериализую класс А, я хотел бы вернуть что-то вроде:

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

где url_to_user не сохраняется в моем экземпляре класса A, но может быть сгенерирован с данными в экземпляре класса A.

Есть ли простой способ сделать это? Я бы предпочел не писать полный сериализатор только для добавления одного поля.

4b9b3361

Ответ 1

Используйте 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);

Ответ 2

Ну, ответ с наивысшим рейтингом довольно быстрый и не очень плохой, если вам не хватает много времени, но вот проблема: нет надлежащего разделения интересов

Вы модифицируете сериализованный JSON в том же месте, где пишете свою бизнес-логику. Вы должны выполнять всю сериализацию внутри TypeAdapter или JsonSerializer.

Как мы можем поддерживать должное разделение интересов?

Ответ заключается в некоторой дополнительной сложности, но архитектура требует этого. Здесь мы идем (взято из моего другого ответа):

Во-первых, мы будем использовать пользовательский сериализатор для типа. Во-вторых, нам нужно создать конструктор копирования внутри базового класса и подкласса оболочки следующим образом:

Примечание: пользовательский сериализатор может показаться излишним, но, поверьте мне, в конечном итоге он окупается за удобство сопровождения. .

// 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);
    }
}

Итак, почему конструктор копирования?

Что ж, после того, как вы определите конструктор копирования, независимо от того, насколько сильно изменится базовый класс, ваша оболочка будет продолжать выполнять ту же роль. Во-вторых, если мы не определим конструктор копирования и просто создадим подкласс базового класса, то нам придется "говорить" в терминах расширенного класса, то есть CatWrapper. Вполне возможно, что ваши компоненты говорят в терминах базового класса, а не типа оболочки.