Поле добавления 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.
Есть ли простой способ сделать это? Я бы предпочел не писать полный сериализатор только для добавления одного поля.
Ответы
Ответ 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
. Вполне возможно, что ваши компоненты говорят в терминах базового класса, а не типа оболочки.