json 使用 gson 解析可变结构的正确方法
Proper way for json parsing for mutable structure with gson
我从服务器得到的响应基于这样的结构:
{
"success":true,
"data":{"can be some kind of data, array or error message"}
}
在这种情况下正确映射数据属性的正确方法是什么?
我的尝试是使用 Any 类型并转换为指定类型:
data class GeneralResponseModel(
val success: Boolean,
val data: Any
)
提供商
//
val response = gson.fromJson(it[0].toString(), GeneralResponseModel::class.java)
//
ViewModel
////////
if (res.success) {
isLoading.postValue(false)
///////
} else {
val result = res.data as ResponseError
errorMessage.postValue(ErrorWrapper(ErrorType.REQUEST_ERROR,result.detail,result.title))
isLoading.postValue(false)
}
///////////
我得到了
io.reactivex.exceptions.OnErrorNotImplementedException:
com.google.gson.internal.LinkedTreeMap cannot be cast to
com.myapp.model.response.ResponseError
另一种尝试是使用由所有可能的响应类型实现的空接口。在这种情况下,我得到了
java.lang.RuntimeException: Unable to invoke no-args constructor for
interface com.myapp.model.response.Response.
Registering an InstanceCreator with Gson for this type may fix this
problem.
我不确定处理这种微不足道的情况的正确方法。任何链接、代码示例或帮助表示赞赏。提前致谢。
更新
感谢 Niklas,我重新考虑了 gson 这样的结构:
lateinit var gson: Gson
when (methodName) {
RequestList.LOGIN.methodName -> {
gson =
GsonBuilder().registerTypeAdapter(
GeneralResponseModel::class.java,
object : JsonDeserializer<GeneralResponseModel> {
override fun deserialize(
json: JsonElement?,
typeOfT: Type?,
context: JsonDeserializationContext?
): GeneralResponseModel {
val gsonInner = Gson()
val jsonObject: JsonObject = json!!.asJsonObject
lateinit var generalResponseModel: GeneralResponseModel
generalResponseModel = if (!jsonObject.get("success").asBoolean) {
GeneralResponseModel(
false,
gsonInner.fromJson(jsonObject.get("data"), ResponseError::class.java)
)
} else {
GeneralResponseModel(
true,
gsonInner.fromJson(jsonObject.get("data"), DriverData::class.java)
)
}
return generalResponseModel
}
}).create()
}
RequestList.GET_JOBS.methodName -> {
gson = GsonBuilder().registerTypeAdapter(
GeneralResponseModel::class.java,
object : JsonDeserializer<GeneralResponseModel> {
override fun deserialize(
json: JsonElement?,
typeOfT: Type?,
context: JsonDeserializationContext?
): GeneralResponseModel {
val gsonInner = Gson()
val jsonObject: JsonObject = json!!.asJsonObject
lateinit var generalResponseModel: GeneralResponseModel
generalResponseModel = if (!jsonObject.get("success").asBoolean) {
GeneralResponseModel(
false,
gsonInner.fromJson(jsonObject.get("data"), ResponseError::class.java)
)
} else {
GeneralResponseModel(
true,
gsonInner.fromJson(jsonObject.get("data"), Array<JobResponse>::class.java)
)
}
return generalResponseModel
}
}).create()
}
else -> gson = Gson()
}
由于您的数据非常通用,因此无法真正以 type-safe 方式进行解析。 Gson 无法基于纯文本推断类型(在您的特定情况下,没有任何信息告诉 Gson 您的数据是 ResponseError)。
我会考虑使用像您一样的通用包装器 class,然后使用 GSON TypeAdapter 来解析对通用包装器的响应。
您必须使用 Builder 实例化 GSON 以定义自定义 TypeAdapter。
registerTypeAdapter(Type type, Object typeAdapter)
配置 Gson 以进行自定义序列化或反序列化。
你的包装器:
public class Response<T> {
T data;
String message;
public Response(T data, String message) {
this.data = data;
this.message = message;
}
boolean hasData() {
return data != null;
}
T getData() {
return data;
}
String getMessage() {
return message;
}
}
初始化:
GsonBuilder builder = new GsonBuilder();
b.registerTypeAdapter(Response.class, new JsonDeserializer<Response>() {
@Override
public Response deserialize(JsonElement arg0, Type arg1,
JsonDeserializationContext arg2) throws JsonParseException {
// ... create Response object here
return response;
}
我从服务器得到的响应基于这样的结构:
{
"success":true,
"data":{"can be some kind of data, array or error message"}
}
在这种情况下正确映射数据属性的正确方法是什么? 我的尝试是使用 Any 类型并转换为指定类型:
data class GeneralResponseModel(
val success: Boolean,
val data: Any
)
提供商
//
val response = gson.fromJson(it[0].toString(), GeneralResponseModel::class.java)
//
ViewModel
////////
if (res.success) {
isLoading.postValue(false)
///////
} else {
val result = res.data as ResponseError
errorMessage.postValue(ErrorWrapper(ErrorType.REQUEST_ERROR,result.detail,result.title))
isLoading.postValue(false)
}
///////////
我得到了
io.reactivex.exceptions.OnErrorNotImplementedException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.myapp.model.response.ResponseError
另一种尝试是使用由所有可能的响应类型实现的空接口。在这种情况下,我得到了
java.lang.RuntimeException: Unable to invoke no-args constructor for interface com.myapp.model.response.Response. Registering an InstanceCreator with Gson for this type may fix this problem.
我不确定处理这种微不足道的情况的正确方法。任何链接、代码示例或帮助表示赞赏。提前致谢。
更新
感谢 Niklas,我重新考虑了 gson 这样的结构:
lateinit var gson: Gson
when (methodName) {
RequestList.LOGIN.methodName -> {
gson =
GsonBuilder().registerTypeAdapter(
GeneralResponseModel::class.java,
object : JsonDeserializer<GeneralResponseModel> {
override fun deserialize(
json: JsonElement?,
typeOfT: Type?,
context: JsonDeserializationContext?
): GeneralResponseModel {
val gsonInner = Gson()
val jsonObject: JsonObject = json!!.asJsonObject
lateinit var generalResponseModel: GeneralResponseModel
generalResponseModel = if (!jsonObject.get("success").asBoolean) {
GeneralResponseModel(
false,
gsonInner.fromJson(jsonObject.get("data"), ResponseError::class.java)
)
} else {
GeneralResponseModel(
true,
gsonInner.fromJson(jsonObject.get("data"), DriverData::class.java)
)
}
return generalResponseModel
}
}).create()
}
RequestList.GET_JOBS.methodName -> {
gson = GsonBuilder().registerTypeAdapter(
GeneralResponseModel::class.java,
object : JsonDeserializer<GeneralResponseModel> {
override fun deserialize(
json: JsonElement?,
typeOfT: Type?,
context: JsonDeserializationContext?
): GeneralResponseModel {
val gsonInner = Gson()
val jsonObject: JsonObject = json!!.asJsonObject
lateinit var generalResponseModel: GeneralResponseModel
generalResponseModel = if (!jsonObject.get("success").asBoolean) {
GeneralResponseModel(
false,
gsonInner.fromJson(jsonObject.get("data"), ResponseError::class.java)
)
} else {
GeneralResponseModel(
true,
gsonInner.fromJson(jsonObject.get("data"), Array<JobResponse>::class.java)
)
}
return generalResponseModel
}
}).create()
}
else -> gson = Gson()
}
由于您的数据非常通用,因此无法真正以 type-safe 方式进行解析。 Gson 无法基于纯文本推断类型(在您的特定情况下,没有任何信息告诉 Gson 您的数据是 ResponseError)。
我会考虑使用像您一样的通用包装器 class,然后使用 GSON TypeAdapter 来解析对通用包装器的响应。
您必须使用 Builder 实例化 GSON 以定义自定义 TypeAdapter。
registerTypeAdapter(Type type, Object typeAdapter)
配置 Gson 以进行自定义序列化或反序列化。
你的包装器:
public class Response<T> {
T data;
String message;
public Response(T data, String message) {
this.data = data;
this.message = message;
}
boolean hasData() {
return data != null;
}
T getData() {
return data;
}
String getMessage() {
return message;
}
}
初始化:
GsonBuilder builder = new GsonBuilder();
b.registerTypeAdapter(Response.class, new JsonDeserializer<Response>() {
@Override
public Response deserialize(JsonElement arg0, Type arg1,
JsonDeserializationContext arg2) throws JsonParseException {
// ... create Response object here
return response;
}