Java 以不区分顺序的方式使用Gson TypeAdapter反序列化JSON对象

Java 以不区分顺序的方式使用Gson TypeAdapter反序列化JSON对象,java,json,gson,Java,Json,Gson,是否有可能实现以下两个目标 能够委托给调用自定义实现的默认Gson反序列化程序 不受JSON对象中键的不同顺序的影响 下面我将介绍两种可能的方法,它们只能实现其中一种 我正在使用的API要么返回一个成功的API,比如: { "type": "success", "data": { "display_name": "Invisible Pink Unicorn", "user_email": "user@example.com", "user_id": 123

是否有可能实现以下两个目标

  • 能够委托给调用自定义实现的默认Gson反序列化程序
  • 不受JSON对象中键的不同顺序的影响
下面我将介绍两种可能的方法,它们只能实现其中一种


我正在使用的API要么返回一个成功的API,比如:

{
  "type": "success",
  "data": {
    "display_name": "Invisible Pink Unicorn",
    "user_email": "user@example.com",
    "user_id": 1234
  }
}
或者一个错误,比如:

{
    "type": "error",
    "data": {
        "error_name": "incorrect_password",
        "error_message": "The username or password you entered is incorrect."
    }
}
目前处理它的方法是注册一个TypeAdapter,如果类型是
“error”
,它会抛出一个异常,并显示给定的
“error\u message”

我看不出有什么办法可以解决这个问题,因为我不认为
JsonReader
有两次读取输入的选项,也没有办法在遇到“type”之后将“data”值缓存在一个抽象类型中,比如
JsonElement

但实际上这是错误的,因为它不使用相同的Gson将工作委托给,所以它将使用不同的字段命名策略,例如

对。您应该使用
JsonDeserializationContext


。。。因为我认为JsonReader没有两次读取输入的选项,所以也没有办法在遇到“type”之后将“data”值缓存在抽象类型(如JsonElement)中进行解析

JsonReader
是流阅读器,而
JsonElement
是树。它们就像XML世界中的SAX和DOM,有各自的优缺点。流式读卡器只读取输入流,您必须自己缓冲/缓存中间数据

对于您的情况,您可以使用这两种方法,但为了简单起见,我选择了
JsonDeserializer
(假设您不打算编写超快速的反序列化程序)

我不确定
用户
APIRERROR
之间的关系,但我会为两种不同类型的值使用一个通用类:真实值和错误。看起来您的两个类有一个共同的父类或祖先类,但我不确定在调用站点(可能是
instanceof
?)如何处理它们。比方说,类似这样(构造函数隐藏以封装对象结构初始化的复杂性):

最终课程内容{
私有最终布尔访问;
私有最终T数据;
私人最终错误;
私有内容(最终布尔值isSuccess、最终T数据、最终ApiError错误){
this.issucess=issucess;
这个数据=数据;
this.error=错误;
}
静态内容成功(最终T数据){
返回新内容(true、data、null);
}
静态内容错误(最终ApiError错误){
返回新内容(false、null、error);
}
布尔值isSuccess(){
返回成功;
}
T getData()
抛出非法状态异常{
如果(!isSuccess){
抛出新的非法状态异常();
}
返回数据;
}
apirerror getError()
抛出非法状态异常{
如果(isSuccess){
抛出新的非法状态异常();
}
返回误差;
}
}
从我的角度来看,
User
apirerror
(虽然我更喜欢
@SerializedName
对命名有更强的控制,但这似乎是一个习惯问题)

final类错误{
@SuppressWarnings(“错误名称”)
最终字符串errorName=null;
@SerializedName(“错误消息”)
最终字符串errorMessage=null;
}
最终类用户{
@SerializedName(“显示名称”)
最终字符串displayName=null;
@SerializedName(“用户\电子邮件”)
最终字符串userEmail=null;
@SuppressWarnings(“用户id”)
final int userId=Integer.valueOf(0);
}
接下来,由于树操作更简单,只需实现JSON反序列化器:

final类ContentJsonDeserializer
实现JsonDeserializer{
//此反序列化程序不保存任何状态
私有静态最终JsonDeserializer contentJsonDeserializer=new contentJsonDeserializer();
私有ContentJsonDeserializer(){
}
//…我们隐藏了这一事实,不允许在呼叫站点上实例化这一点
静态JsonDeserializer getContentJsonDeserializer(){
//缩小@SuppressWarnings作用域--抑制整个方法的警告可能是有害的
@抑制警告(“未选中”)
最终JsonDeserializer contentJsonDeserializer=(JsonDeserializer)contentJsonDeserializer.contentJsonDeserializer;
返回contentJsonDeserializer;
}
@凌驾
公共内容反序列化(最终JsonElement JsonElement、最终类型类型、最终JsonDeserializationContext上下文)
抛出JsonParseException{
final JsonObject JsonObject=jsoneelement.getAsJsonObject();
最终字符串responseType=jsonObject.getAsJsonPrimitive(“类型”).getAsString();
开关(响应类型){
“成功”案例:
返回成功(context.deserialize(jsonObject.get(“data”)、getTypeParameter0(type));
案例“错误”:
返回错误(context.deserialize(jsonObject.get(“数据”),apierro.class));
违约:
抛出新的JsonParseException(responseType);
}
}
//尝试为其第一个类型参数检测任何给定的类型参数化
私有静态类型getTypeParameter0(最终类型){
if(!(参数化类型的类型instanceof)){
返回Object.class;
}
返回((ParameteredType)类型).getActualTypeArguments()[0];
}
}
演示:

private static final Gson Gson=new GsonBuilder()
.registerTypeAdapter(Content.class,getContentJsonDeserializer())
.create();
私有静态最终类型userContent=newtypetoken(){
}.getType();
P
new GsonBuilder()
    .registerTypeAdapter(User.class, new ContentDeserializer<User>())
    .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
    .create()

public class ContentDeserializer<T> implements JsonDeserializer<T> {
    @Override
    public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        final JsonObject object = json.getAsJsonObject();
        final String type = object.get("type").getAsString();
        final JsonElement data = object.get("data");
        final Gson gson = new Gson();
        if ("error".equals(type)) {
            throw gson.fromJson(data, ApiError.class);
        } else {
            return gson.fromJson(data, typeOfT);
        }
    }
}
public class UserAdapterFactory implements TypeAdapterFactory {

    @SuppressWarnings("unchecked")
    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        if (!User.class.isAssignableFrom(type.getRawType())) return null;
        final TypeAdapter<User> userAdapter = (TypeAdapter<User>) gson.getDelegateAdapter(this, type);
        final TypeAdapter<ApiError> apiErrorAdapter = gson.getAdapter(ApiError.class);
        return (TypeAdapter<T>) new Adapter(userAdapter, apiErrorAdapter);
    }

    private static class Adapter extends TypeAdapter<User> {
        private final TypeAdapter<User> userAdapter;
        private final TypeAdapter<ApiError> apiErrorAdapter;

        Adapter(TypeAdapter<User> userAdapter, TypeAdapter<ApiError> apiErrorAdapter) {
            this.userAdapter = userAdapter;
            this.apiErrorAdapter = apiErrorAdapter;
        }

        @Override
        public void write(JsonWriter out, User value) throws IOException {
        }

        @Override
        public User read(JsonReader in) throws IOException {
            User user = null;
            String type = null;
            in.beginObject();
            while (in.hasNext()) {
                switch (in.nextName()) {
                    case "type":
                        type = in.nextString();
                        break;
                    case "data":
                        if ("error".equals(type)) {
                            throw apiErrorAdapter.read(in);
                        } else if ("success".equals(type)) {
                            user = userAdapter.read(in);
                        }
                        break;
                }
            }
            in.endObject();
            return user;
        }
    }
}
{
  "data": {
    "display_name": "Invisible Pink Unicorn",
    "user_email": "user@example.com",
    "user_id": 1234
  },
  "type": "success"
}
private static final Gson gson = new GsonBuilder()
        .registerTypeAdapter(Content.class, getContentJsonDeserializer())
        .create();

private static final Type userContent = new TypeToken<Content<User>>() {
}.getType();

public static void main(final String... args)
        throws IOException {
    for ( final String name : ImmutableList.of("success.json", "error.json", "success-reversed.json", "error-reversed.json") ) {
        try ( final JsonReader jsonReader = getPackageResourceJsonReader(Q44400163.class, name) ) {
            final Content<User> content = gson.fromJson(jsonReader, userContent);
            if ( content.isSuccess() ) {
                System.out.println("SUCCESS: " + content.getData().displayName);
            } else {
                System.out.println("ERROR:   " + content.getError().errorMessage);
            }
        }
    }
}