Как изменить результирующий Json при сериализации, более или менее глобально, если объявления классов не могут быть изменены? - PullRequest
0 голосов
/ 08 ноября 2018

Предположим, что:

  • есть связка объектов Java, которые необходимо передать - например, в какой-то API
  • вы не хотите или не можете изменить объявление для этих объектов
  • к сожалению API требует чего-то, что не объявлено в этих объектах

В качестве примера (вдохновленный этим вопросом) приведен простой класс:

@Getter
@RequiredArgsConstructor
public class Login {
    private final String username, password; 
}

Однако API ожидает, что JSON будет выглядеть так:

{
  "username": "uname",
  "password": "pword",
  "version": 1
}

и эта же проблема относится ко всем остальным 99 классам: им также необходимо поле version со значением 1 в сериализованном JSON.

Есть некоторые решения, которые требуют либо низкоуровневой манипуляции со строками, либо добавления большого количества шаблонного кода, но каков будет универсальный способ решения этой проблемы с GSON?

1 Ответ

0 голосов
/ 08 ноября 2018

Во-первых, , чтобы Gson мог сериализовать (или десериализовать) группу объектов разных типов, имеющих одинаковый тип адаптации, наилучшим способом избежать регистрации большого количества адаптеров или изменения объявлений классов - это воспользоваться преимуществами TypeAdapterFactory.

Он не привязан ни к какому Type или Class, но решает для каждого типа, который TypeAdapter будет возвращаться, когда Gson сталкивается с каким-либо объектом для сериализации (или десериализации). Использование TypeAdaterFactory освобождает код от регистрации лотов TypeAdapter s.

Во-вторых и, естественно, чтобы избежать создания многих TypeAdapter s, решение состоит в том, чтобы по возможности создать общий тип TypeAdapter.

Начиная с обычного TypeAdapter в вопросах, это может выглядеть так:

@RequiredArgsConstructor
private class GenericTypeAdapter<T> extends TypeAdapter<T> {

    // typeToken is needed when deserializing
    private final TypeToken<T> typeToken;
    private final Gson gson = new GsonBuilder().setPrettyPrinting().create();

    @Override
    public void write(JsonWriter out, T value) throws IOException {
        // Altering could be done with some low level string manipulation
        // but JsonObject makes altering object more safe.
        // Feel free to comment for better way to instantiate it,
        // this is just for an example.
        JsonObject jsonObject = gson.fromJson(gson.toJson(value)
                            ,JsonElement.class).getAsJsonObject();
        // alter jsonObject in any way needed,
        // here is only added version information
        jsonObject.addProperty("version", 1);
        out.jsonValue(gson.toJson(jsonObject));
    }

    @Override
    public T read(JsonReader in) throws IOException {
        // maybe needless to mention but mention still:
        // here it is possible to init object in a way 
        // that provided JSON solely does not make possible
        return gson.fromJson(in, typeToken.getType());
    }
}

Тогда TypeAdapterFactory. Это довольно просто, но обратите внимание на комментарии в примере кода. Как упоминалось ранее, TypeAdapterFactory отвечает за возвращение правильных TypeAdapter для объекта. Хотя он предназначен для применения к связке типов, он не должен применяться к всем типам. Самый простой TypeAdapterFactory:

public class GenericTypeAdapterFactory implements TypeAdapterFactory {
    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        // here should be checked if the passed type needs any custom 
        // adapter and if it needs then decide  which adapter to return
        // or in case of no customization needed return null for default
        // adapter.
        // decision can be made for example by
        // * type itself
        // * package name
        // * if type implements / extends some super type
        return new GenericTypeAdapter<>(type);
    }       
}

Использование тогда будет просто:

Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(new GenericTypeAdapterFactory())
    .setPrettyPrinting()
    .create()

Примечание : я подготовил этот ответ первоначально к к этому вопросу , но, поскольку позже он, по-видимому, основывался на котлине (?), Я чувствовал себя лучше для создания более общих вопросов и ответов, касающихся Java.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...