【问题标题】:How to invoke default deserialize with gson如何使用 gson 调用默认反序列化
【发布时间】:2019-03-04 22:28:15
【问题描述】:

我得到一个具有“字段”字段的 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;
                }
            }
        };
    }
}

【问题讨论】:

  • 在数组可用时粘贴要反序列化数据的代码
  • 阵列不可用。它只能是空的。如果我需要反序列化数组,我会在 catch 块中进行。

标签: java android gson json


【解决方案1】:

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

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

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

【讨论】:

  • +1 for getDelegateAdapter() 这是实现通用功能的好解决方案,与对象类型无关。例如,在对象上调用后反序列化函数。
【解决方案2】:
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;
    }
}

【讨论】:

  • 这种方法避免了无限递归,因为您正在使用一个没有注册适配器的新 Gson 对象。在这种情况下,您也可以对其调用反序列化。
  • 如此简单。太棒了。
  • 这个问题是,通常你会在你的 Gson 实例上拥有你想要的其他特殊规则和适配器。这会创建一个没有您的应用特定规则的新默认值
  • 还是StackOverflowError
【解决方案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 是解决此问题的完美解决方案。我只是相信它比你需要的更多。

【讨论】:

    【解决方案4】:

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

    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-06
      • 2019-11-24
      • 2013-02-01
      • 1970-01-01
      • 2018-07-20
      • 2021-11-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多