Gson:反序列化动态字段

r55awzrz  于 2022-11-06  发布在  其他
关注(0)|答案(2)|浏览(193)

我使用了一个非常奇怪的api,它的数据字段类型是动态的。
如果发生错误,数据字段将是如下字符串:

{
  "code": 2001,
  "data": "Error!"
}

而如果成功,则数据字段将是一个对象:

{
  "code": 2000,
  "data": {
    "id": 1,
    "name": "example"
  }
}

我使用下面的Kotlin代码来反序列化它:

return Gson().fromJson(data, SimpleModel::class.java)

模型定义如下:

import com.google.gson.annotations.SerializedName

class SimpleModel {
    @SerializedName("code")
    val code = 0

    @SerializedName("data")
    val data: SimpleData = SimpleData()
}

class SimpleData {
    @SerializedName("id")
    val id = ""

    @SerializedName("name")
    val name = ""
}

当没有错误发生时,上面的代码工作正常。但是当错误发生时,抛出异常:

java.lang.IllegalStateException: Excepted BEGIN_OBJECT but was STRING at line 1 column x $path.data

是否有一种方法可以将data字段反序列化为一个对象或任何对象,并通过代码手动确定其类型?

xmakbtuz

xmakbtuz1#

您需要编写一个自定义的反序列化器,它根据节点的运行时类型决定将data反序列化为什么类型,并将该反序列化器注册到您的Gson示例中。不幸的是,我不熟悉Kotlin语法,所以我只能给予您伪代码。

  1. SimpleModel中的字段data应该是Object,或者使类成为泛型-SimpleModel<T>,并且该字段的类型也应该是T
    1.将输入解析为gson的节点类型-JsonElement
    1.获取数据字段
JsonElement root = parseResponse();
root.getAsJsonObject().get("data").getAsString();

1.使用getAs...()方法检查类型。
1.以字符串形式获取。如果成功,则为字符串,并在SimpleModel中设置字符串值。
1.如果以字符串的形式获取时出现异常,则以对象-getAsJsonObject()的形式获取它,将该对象解析为SimpleData,并在SimpleModel中设置此新对象。
你可以用我的答案作为灵感,虽然它是关于对象Map器的,但它做的是同样的事情--根据节点类型来决定对象类型,并遵循与上面描述的大致相同的算法。
此外,this guide有关于如何编写自己的反序列化器和注册它的信息。

wfsdck30

wfsdck302#

和前面的答案一样,我对Kotlin并不熟悉,下面的解决方案是用Java编写的,但据我所知,使用IntelliJ内置工具将Java转换为Kotlin是很容易的。
success/error对象对是一个典型的问题,您可以创建自己的方法来解决它,但是让我们考虑以下类分别表示success和error对象(Java 17,启用交换机上的模式匹配):

abstract sealed class SimpleModel<T>
        permits SimpleModelSuccess, SimpleModelError {

    @SerializedName("code")
    final int code;

    SimpleModel(final int code) {
        this.code = code;
    }

}

final class SimpleModelSuccess<T>
        extends SimpleModel<T> {

    @SerializedName("data")
    final T data;

    private SimpleModelSuccess(final int code, final T data) {
        super(code);
        this.data = data;
    }

}

final class SimpleModelError<T>
        extends SimpleModel<T> {

    @SerializedName("data") // the annotation is helping here!
    final String message;

    private SimpleModelError(final int code, final String message) {
        super(code);
        this.message = message;
    }

}

上面的代码可以自我解释。现在的核心部分,需要更多的工作比我以前认为,通过提供你我的第一个评论,似乎不完整。
第一个
下面是它输出到stdout的内容:
简单模型类型适配器工厂测试.简单数据(标识=1,名称=示例)
出错了!
嗯,是的,这是一个“有点”比我的第一个评论所建议的更棘手。

相关问题