我得到一个有“字段”字段的json。
如果“字段”有数据,那么有一个OBJECT有许多(大约20个)其他字段也是对象。我可以毫无问题地反序列化它们。
但是如果“field”没有数据,那么它就是一个空的ARRAY(我知道它很疯狂,但那是服务器的响应而我无法改变它)。
像这样:
空时:
"extras":[
]
有一些数据:
"extras": {
"22":{ "name":"some name" },
"59":{ "name":"some other name" },
and so on...
}
所以,如果没有数据(空数组),我显然会得到例外
Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:
Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 4319
我尝试使用自定义JavaDeserializer:
public class ExtrasAdapter implements JsonDeserializer<Extras> {
@Override
public Extras deserialize(JsonElement json, Type typeOf,
JsonDeserializationContext context) throws JsonParseException {
try {
JsonObject jsonObject = json.getAsJsonObject();
// deserialize normally
// the following does not work, as it makes recursive calls
// to the same function
//return context.deserialize(jsonObject,
// new TypeToken<Object>(){}.getType());
} catch (IllegalStateException e) {
return null;
}
}
}
我按照以下方式阅读json
Gson gsonDecoder = new GsonBuilder().registerTypeAdapter(Extras.class, new ExtrasAdapter();
// httpResponse contains json with extras filed.
Reader reader = new InputStreamReader(httpResponse.getEntity().getContent());
Extras response = gsonDecoder.fromJson(reader, Extras.class);
我不想手动反序列化所有20个字段(我知道这是一个选项),我只想调用context.defaultDeserialize()或类似的东西。
再一次:我没有任何问题反序列化普通的json,创建自定义对象,注册自定义TypeAdapter,自定义JavaDeserializers。这一切都已经有效了。我只需要一些处理数据的解决方案,可以是ARRAY和OBJECT。
谢谢你的帮助。
======================
乔伊的回答非常完美。这是我正在寻找的东西。
我会在这里发布我的代码。
public class SafeTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
return new TypeAdapter<T>() {
public void write(JsonWriter out, T value) throws IOException {
try {
delegate.write(out, value);
} catch (IOException e) {
delegate.write(out, null);
}
}
public T read(JsonReader in) throws IOException {
try {
return delegate.read(in);
} catch (IOException e) {
Log.w("Adapter Factory", "IOException. Value skipped");
in.skipValue();
return null;
} catch (IllegalStateException e) {
Log.w("Adapter Factory", "IllegalStateException. Value skipped");
in.skipValue();
return null;
} catch (JsonSyntaxException e) {
Log.w("Adapter Factory", "JsonSyntaxException. Value skipped");
in.skipValue();
return null;
}
}
};
}
}
答案 0 :(得分:22)
尝试使用GSON&gt; = 2.2.1并查找TypeAdapterFactory类。
这将使您能够在反序列化之前检查对象并应用自定义代码,同时避免递归。
以下是您可以使用的getDelegateAdapter示例。
答案 1 :(得分:12)
public class ExtrasAdapter implements JsonDeserializer<Extras> {
@Override
public Extras deserialize(JsonElement json, Type typeOf,
JsonDeserializationContext context) throws JsonParseException {
try {
JsonObject jsonObject = json.getAsJsonObject();
return new Gson().fromJson(jsonObject , Extras.class); // default deserialization
} catch (IllegalStateException e) {
return null;
}
}
答案 2 :(得分:3)
对于迟到的人来说,你不需要实现TypeAdapter来解决这个问题,尽管这样做是一个非常有效的解决方案。
这个问题的答案实际上是在最初的问题中:
public class ExtrasAdapter implements JsonDeserializer<Extras> {
@Override
public Extras deserialize(JsonElement json, Type typeOf,
JsonDeserializationContext context) throws JsonParseException {
try {
JsonObject jsonObject = json.getAsJsonObject();
// deserialize normally
// the following does not work, as it makes recursive calls
// to the same function
//return context.deserialize(jsonObject, new TypeToken<Object>(){}.getType());
} catch (IllegalStateException e) {
return null;
}
}
注释掉了
return context.deserialize(jsonObject, new TypeToken<Object>(){}.getType());
几乎是解决方案。这个问题有两个问题。首先,jsonObject是最初传递给该函数的确切对象。
JsonObject jsonObject = json.getAsJsonObject();
因此将其传递给context.deserialize()将创建递归,并最终创建OOM。这里的解决方案是解析jsonObject中的对象。
这引出了我们的第二个问题,即有两件事情在这里混合。 “Extras”是一种对象类型,可能是支持它的具体类(可能是一个空数组)。 “额外”是地图。试图将“Extra”解析为“Extras”是行不通的。为此,我建议以下“额外”的定义:
public class Extras {
Map<String, Map<String, String>> extras;
// you could also create a concrete class for "Extra"
//and have this be a Map<String, Extra>
}
在这种情况下,使用context.deserialize解决问题变得微不足道。
如上所述,TypeAdatper是解决此问题的完美有效解决方案。我只相信这比你需要的更多。
答案 3 :(得分:0)
我根据需要将空数组反序列化为null,但仅针对我指定的类,创建了一个替代的TypeAdapter:
class EmptyArraysAsNullTypeAdapterFactory @Inject constructor() : TypeAdapterFactory {
companion object {
// Add classes here as needed
private val classesAllowedEmptyArrayAsNull = arrayOf(Location::class.java,
Results::class.java)
private fun isAllowedClass(rawType: Class<*>): Boolean {
return classesAllowedEmptyArrayAsNull.find { rawType.isAssignableFrom(it) } != null
}
}
override fun <T> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
val delegate = gson.getDelegateAdapter(this, type)
val rawType = type.rawType as Class<T>
return object : TypeAdapter<T>() {
override fun write(out: JsonWriter, value: T?) {
delegate.write(out, value)
}
override fun read(reader: JsonReader): T? {
return if (reader.peek() === JsonToken.BEGIN_ARRAY && isAllowedClass(rawType)) {
reader.beginArray()
// If the array is empty, assume it is signifying null
if (!reader.hasNext()) {
reader.endArray()
null
} else {
throw JsonParseException("Not expecting a non-empty array when deserializing: ${type.rawType.name}")
}
} else {
delegate.read(reader)
}
}
}
}
}