How to invoke default deserialize with gson

前端 未结 4 1669
隐瞒了意图╮
隐瞒了意图╮ 2020-12-25 11:57

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

相关标签:
4条回答
  • 2020-12-25 11:59

    Try using GSON >= 2.2.1 and look for the TypeAdapterFactory class.

    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 getDelegateAdapter you can use.

    0 讨论(0)
  • 2020-12-25 12:09

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

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

    }

    0 讨论(0)
  • 2020-12-25 12:11

    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.

    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 = 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.

    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":

    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.

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

    0 讨论(0)
  • 2020-12-25 12: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;
        }
    }
    
    0 讨论(0)
提交回复
热议问题