假使,假设:


您需要传递一堆Java对象-例如-传递给某些API
您不愿意或无法更改这些对象的声明
不幸的是,API需要在那些对象中未声明的内容


作为一个示例(受this问题启发),有一个简单的类:

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


但是,API需要JSON,例如:

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


相同的问题也适用于所有其他99个类:那些还需要序列化JSON中值为1的字段version

有一些解决方案需要底层字符串操作或添加大量样板代码,但是用GSON处理此问题的通用方法是什么?

最佳答案

首先,让Gson序列化(或反序列化)一堆具有相同类型的不同类型对象的最佳方法是避免注册大量适配器或更改类声明,这是利用TypeAdapterFactory的好处。

它本身并不绑定到任何TypeClass,而是根据类型决定当Gson碰到要序列化(或反序列化)的对象时要返回哪个TypeAdapter。使用TypeAdaterFactory使代码免于注册大量TypeAdapter

其次,自然而然地,为了避免创建许多TypeAdapter,解决方案是在可能的情况下使通用类型为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()


注意:我最初是为this question准备这个答案的,但是由于后来似乎是基于Kotlin(?)的,我觉得最好创建一个更通用的Java问答。

10-07 12:43