基 class 的 Gson 自定义反序列化器

Gson custom deserializer for base class

我需要使用 REST API 并且我正在使用 Gson,如果我的模型中的几十个 类 不需要自定义 Gson 反序列化器,那就太好了。

我认为我应该使用自定义 TypeAdapterFactory,但文档很差,我很难过。

我感兴趣的 类 或多或少遵循以下模式:

public class APIResource {
    @SerializedName("id")
    private Integer id;
    //Constructor and getter
}

public class B extends APIResource {
    @SerializedName("field")
    String field;
    @SerializedName("resources")
    List<APIResource> resourceList;
    //Constructor and getter
}

public class C extends B {
    @SerializedName("other_fields")
    List<Object> otherFieldList;
    @SerializedName("resource")
    APIResource resource;
    @SerializedName("b_list")
    List<B> bList;
    //Constructor and getter
}

有时 id 作为一个名为 "url" 的字符串包含在 JSON 中,我必须对其进行解析。

JSON非常复杂,包含多个对象和数组,它们的结构几乎是随机的。

"url" 名称可以在 JSON 中的任何位置,我无法使用 beginObject()beginArray()

使其工作

我认为我的自定义 TypeAdapterFactory 应该是这样的

public class ResourceTypeAdapterFactory implements TypeAdapterFactory {
    @Override
    public <T> TypeAdapter<T> create(Gson gson, @NonNull TypeToken<T> type) {
        if (!APIResource.class.isAssignableFrom(type.getRawType())) {
            return null;
        }
        TypeAdapter<T> defaultTypeAdapter = gson.getDelegateAdapter(this, type);

        return new TypeAdapter<T>() {

            @Override
            public void write(JsonWriter out, T value) throws IOException {
                defaultTypeAdapter.write(out, value);
            }

            @Override
            public T read(JsonReader in) throws IOException {
                //if the name is "url" use the urlToId method, else
                return defaultTypeAdapter.read(in);
            }
        }.nullSafe();

    }

    Integer urlToId(String url) {
        Matcher matcher = Pattern
                .compile("/-?[0-9]+/$")
                .matcher(url);
        return matcher.find() ?
                Integer.valueOf(matcher.group().replace("/","")):
                null;
    }

}

我解决了,如果有人遇到同样的问题,这就是我的解决方案

public class ResourceTypeAdapterFactory implements TypeAdapterFactory {
    @Override
    public <T> TypeAdapter<T> create(Gson gson, @NonNull TypeToken<T> type) {
        if (!APIResource.class.isAssignableFrom(type.getRawType())) {
            return null;
        }
        final TypeAdapter<T> delegateAdapter = gson.getDelegateAdapter(this, type);
        final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);

        return new TypeAdapter<T>() {

            @Override
            public void write(JsonWriter out, T value) throws IOException {
                delegateAdapter.write(out, value);
            }

            @Override
            public T read(JsonReader in) throws IOException {
                JsonElement tree = elementAdapter.read(in);
                afterRead(tree);
                return delegateAdapter.fromJsonTree(tree);
            }

            protected void afterRead(@NonNull JsonElement jsonElement) {
                if(jsonElement instanceof JsonObject) {
                    JsonObject jsonObject = ((JsonObject)jsonElement);
                    for(Map.Entry<String,JsonElement> entry : jsonObject.entrySet()){
                        if(entry.getValue() instanceof JsonPrimitive) {
                            if(entry.getKey().equalsIgnoreCase("url")) {
                                String val = jsonObject.get(entry.getKey()).toString();
                                jsonObject.addProperty("id", urlToId(val));
                            }
                        } else {
                            afterRead(entry.getValue());
                        }
                    }
                }
            }

        }.nullSafe();

    }

    Integer urlToId(@NonNull String url) {
        Matcher matcher = Pattern
                .compile("/-?[0-9]+/$")
                .matcher(url.replace("\"", ""));
        return matcher.find() ?
                Integer.valueOf(matcher.group().replace("/","")):
                null;
    }

}