How to change resulting Json when serializing, more or less globally, if class declarations can not be changed?

…衆ロ難τιáo~ 提交于 2019-12-24 20:52:15

问题


Assume that:

  • there is a bunch of Java objects that you need to pass - for example - to some API
  • you are not willing or can not change declaration for those objects
  • unfortunately API requires something that is not declared in those objects

As an example (inspired by this question) there is a simple class:

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

However, API expects JSON like:

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

and this same problem applies to all other 99 classes: those need also field version with value 1 in serialized JSON.

There are some solutions that require either low level string manipulation or adding lots of boilerplate code but what would be the generic way to deal with this issue with GSON?


回答1:


Firstly, to have Gson to serialize (or to deserialize) bunch of different types of objects having same type of adapting the best way to avoid registering lots of adapters or altering class declarations is to make benefit of TypeAdapterFactory.

It is not itself bound to any Type or Class but decides per type which TypeAdapter to return when Gson bumps into some object to serialize (or deserialize). Using TypeAdaterFactory frees code from registering lots of TypeAdapters.

Secondly and naturally, to avoid creating many TypeAdapters the solution is to make a generic type of TypeAdapter whenever possible.

Starting from the generic TypeAdapter in questions case it might be like:

@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());
    }
}

Then the TypeAdapterFactory. It is quite simple but pay attention to the comments in example code. As mentioned before TypeAdapterFactory is responsible of returning correct TypeAdapter per object. Although it is meant to apply to a bunch of types it might not be meant to apply to all the types. Most simple 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);
    }       
}

Usage would then simply be:

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

Note: I prepared this answer originally to this question but since it later appeared to be Kotlin(?) based I felt better to create a more generic Q&A dealt with Java.



来源:https://stackoverflow.com/questions/53209467/how-to-change-resulting-json-when-serializing-more-or-less-globally-if-class-d

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!