31

我得到一个具有“字段”字段的 json。
如果“字段”有数据,那么就有一个 OBJECT,其中有许多(大约 20 个)也是对象的其他字段。我可以毫无问题地反序列化它们。
但是如果“字段”没有数据,它就是一个空数组(我知道这很疯狂,但这是来自服务器的响应,我无法更改它)。像这样的东西:

空时:

"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、自定义 JavaDeserializer 方面没有任何问题。这一切都已经奏效了。我只需要一些处理数据的解决方案,既可以是数组也可以是对象。
谢谢你的帮助。

======================


乔伊的答案很完美。这就是我要找的东西。我将在这里发布我的代码。

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;
                }
            }
        };
    }
}
4

4 回答 4

24

尝试使用 GSON >= 2.2.1 并查找TypeAdapterFactory类。

这将使您能够在反序列化之前检查对象并应用自定义代码,同时避免递归。

这是您可以使用的getDelegateAdapter示例。

于 2012-05-20T23:03:51.713 回答
13
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;
    }
}
于 2015-11-24T08:51:04.980 回答
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 是解决此问题的完美解决方案。我只是相信它比你需要的更多。

于 2016-11-15T12:27:19.243 回答
0

我根据我的需要创建了一个替代 TypeAdapter,让空数组反序列化为 null,但仅适用于我指定的类:

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)
            }
        }
    }
}

}

于 2019-03-04T14:28:15.887 回答