java 如何使用 gson 调用默认反序列化

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10596667/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 01:45:25  来源:igfitidea点击:

How to invoke default deserialize with gson

javaandroidgsonjson

提问by Anton Rutkevich

I get a json that has "field" field.
If the "field" has data, then there is an OBJECT that has many (about 20) other fields that are also objects. I can deserialize them without any problems.
But if "field" has no data, it is an empty ARRAY (I know it's crazy, but that's the response from server and I can't change it). Something like this:

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

When empty:

空时:

"extras":[

]

Has some data:

有一些数据:

"extras": {
    "22":{ "name":"some name" },
    "59":{ "name":"some other name" },
    and so on...
}

So, when there if no data (empty array), I obviously get the exception

所以,如果没有数据(空数组),我显然得到了异常

Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:
Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 4319

I tried to use custom JavaDeserializer:

我尝试使用自定义 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;
        }
    }
}

I read the json the following way

我通过以下方式阅读了 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);

I don't want to deserialize all 20 fields manually (I know this is an option), I just want to call context.defaultDeserialize(), or something like that.
Once again: I don't have any problems deserializing normal json, creating custom objects, registering custom TypeAdapters, custom JavaDeserializers. It all works already. I need only some solution for handling a data, that can be both ARRAY and OBJECT.
Thanks for any help.

我不想手动反序列化所有 20 个字段(我知道这是一个选项),我只想调用 context.defaultDeserialize() 或类似的东西。
再说一遍:我在反序列化普通 json、创建自定义对象、注册自定义 TypeAdapter、自定义 JavaDeserializers 时没有任何问题。这一切都已经奏效了。我只需要一些处理数据的解决方案,它可以是 ARRAY 和 OBJECT。
谢谢你的帮助。

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


The Joey's answer works perfect. That right the thing I was looking for. I'll post my code here.

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


乔伊的回答完美无缺。这正是我正在寻找的东西。我会在这里发布我的代码。

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

采纳答案by Joey

Try using GSON >= 2.2.1 and look for the TypeAdapterFactoryclass.

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

This will give you the ability to inspect the Object before you deserialize it and apply custom code while avoiding recursions.

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

Here is an example of the getDelegateAdapteryou can use.

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

回答by Rahul

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

回答by MrMannWood

For anyone coming in late, you don't need to implement a TypeAdapter to solve this problem, although doing so is a perfectly valid solution.

对于迟到的任何人,您不需要实现 TypeAdapter 来解决这个问题,尽管这样做是一个非常有效的解决方案。

The answer to this problem is actually in the original question:

这个问题的答案其实在原题中:

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

The commented out

注释掉的

return context.deserialize(jsonObject, new TypeToken<Object>(){}.getType());

is almost the solution. The issue is two fold. First, jsonObject is the exact object passed into this function originally.

几乎是解决方案。这个问题有两个方面。首先,jsonObject 是最初传入这个函数的确切对象。

JsonObject jsonObject = json.getAsJsonObject();

So passing it into context.deserialize() will create recursion, and eventually OOM. The solution here is to parse out the objects inside of jsonObject.

所以将它传递给 context.deserialize() 将创建递归,并最终 OOM。这里的解决方法是解析出jsonObject里面的对象。

This leads us to the second problem, which is that there are two things getting mixed here. "Extras" is an object type, presumably with a concrete class backing it (and possibly an empty array). "Extra" is a map. Attempting to parse an "Extra" as an "Extras" isn't going to work. To that end, I would suggest the following definition of "Extras":

这将我们引向第二个问题,即这里混合了两件事。“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>
}

In which case the problem becomes trivial for solving with context.deserialize.

在这种情况下,使用 context.deserialize 解决问题就变得微不足道了。

As I stated above, a TypeAdatper is a perfectly valid solution for this problem. I just believe that it's more than you need.

正如我上面所说的,TypeAdatper 是解决这个问题的完美有效的解决方案。我只是相信它比你需要的更多。

回答by Carson Holzheimer

I created an alternative TypeAdapter based on my needs to let empty arrays deserialize to null, but only for the classes I specify:

我根据我的需要创建了一个替代 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)
            }
        }
    }
}

}

}