用gson对abstract class (de)serialize

用gson对abstract class (de)serialize时,不能直接

Gson gson = new Gson();

List<BaseClass> list = gson.fromJson(string, new TypeToken<List<BaseClass>>(){}.getType());

否则会报错:

java.lang.RuntimeException: Failed to invoke public xxx.BaseClass() with no args

可以用RunTimeTypeAdapterFactor:

RuntimeTypeAdapterFactory<BaseClass> typeAdapterFactory = RuntimeTypeAdapterFactory

    .of\(BaseClass.class\).registerSubtype\(SubClass1.class\).registerSubtype\(SubClass2.class\);

Gson gson = new GsonBuilder().registerTypeAdapterFactory(typeAdapterFactory).create();

List<BaseClass> list = gson.fromJson(string, new TypeToken<List<BaseClass>>(){}.getType());

ref: https://futurestud.io/tutorials/how-to-deserialize-a-list-of-polymorphic-objects-with-gson

但是运行时还是报错:

com.google.gson.JsonParseException: cannot deserialize class xxx.BaseClass because it does not define a field named type

解决方法:

在RunTimeTypeAdapterFactor.java中把 if (type.getRawType() != baseType)改为:

if (null == type || !baseType.isAssignableFrom(type.getRawType()))

ref: https://github.com/google/gson/issues/712

RunTimeTypeAdapterFactor.java: (https://github.com/google/gson/blob/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java)

import com.google.gson.Gson;

import com.google.gson.JsonElement;

import com.google.gson.JsonObject;

import com.google.gson.JsonParseException;

import com.google.gson.JsonPrimitive;

import com.google.gson.TypeAdapter;

import com.google.gson.TypeAdapterFactory;

import com.google.gson.internal.Streams;

import com.google.gson.reflect.TypeToken;

import com.google.gson.stream.JsonReader;

import com.google.gson.stream.JsonWriter;

import java.io.IOException;

import java.util.LinkedHashMap;

import java.util.Map;

public class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {

private final Class&lt;?&gt; baseType;

private final String typeFieldName;

private final Map&lt;String, Class&lt;?&gt;&gt; labelToSubtype = new LinkedHashMap&lt;String, Class&lt;?&gt;&gt;\(\);

private final Map&lt;Class&lt;?&gt;, String&gt; subtypeToLabel = new LinkedHashMap&lt;Class&lt;?&gt;, String&gt;\(\);



private RuntimeTypeAdapterFactory\(Class&lt;?&gt; baseType, String typeFieldName\) {

    if \(typeFieldName == null \|\| baseType == null\) {

        throw new NullPointerException\(\);

    }

    this.baseType = baseType;

    this.typeFieldName = typeFieldName;

}



/\*\*

 \* Creates a new runtime type adapter using for {@code baseType} using {@code

 \* typeFieldName} as the type field name. Type field names are case sensitive.

 \*/

public static &lt;T&gt; RuntimeTypeAdapterFactory&lt;T&gt; of\(Class&lt;T&gt; baseType, String typeFieldName\) {

    return new RuntimeTypeAdapterFactory&lt;T&gt;\(baseType, typeFieldName\);

}



/\*\*

 \* Creates a new runtime type adapter for {@code baseType} using {@code "type"} as

 \* the type field name.

 \*/

public static &lt;T&gt; RuntimeTypeAdapterFactory&lt;T&gt; of\(Class&lt;T&gt; baseType\) {

    return new RuntimeTypeAdapterFactory&lt;T&gt;\(baseType, "type"\);

}



/\*\*

 \* Registers {@code type} identified by {@code label}. Labels are case

 \* sensitive.

 \*

 \* @throws IllegalArgumentException if either {@code type} or {@code label}

 \*     have already been registered on this type adapter.

 \*/

public RuntimeTypeAdapterFactory&lt;T&gt; registerSubtype\(Class&lt;? extends T&gt; type, String label\) {

    if \(type == null \|\| label == null\) {

        throw new NullPointerException\(\);

    }

    if \(subtypeToLabel.containsKey\(type\) \|\| labelToSubtype.containsKey\(label\)\) {

        throw new IllegalArgumentException\("types and labels must be unique"\);

    }

    labelToSubtype.put\(label, type\);

    subtypeToLabel.put\(type, label\);

    return this;

}



/\*\*

 \* Registers {@code type} identified by its {@link Class\#getSimpleName simple

 \* name}. Labels are case sensitive.

 \*

 \* @throws IllegalArgumentException if either {@code type} or its simple name

 \*     have already been registered on this type adapter.

 \*/

public RuntimeTypeAdapterFactory&lt;T&gt; registerSubtype\(Class&lt;? extends T&gt; type\) {

    return registerSubtype\(type, type.getSimpleName\(\)\);

}



public &lt;R&gt; TypeAdapter&lt;R&gt; create\(Gson gson, TypeToken&lt;R&gt; type\) {

    if \(null == type \|\| !baseType.isAssignableFrom\(type.getRawType\(\)\)\) {

        return null;

    }



    final Map&lt;String, TypeAdapter&lt;?&gt;&gt; labelToDelegate

            = new LinkedHashMap&lt;String, TypeAdapter&lt;?&gt;&gt;\(\);

    final Map&lt;Class&lt;?&gt;, TypeAdapter&lt;?&gt;&gt; subtypeToDelegate

            = new LinkedHashMap&lt;Class&lt;?&gt;, TypeAdapter&lt;?&gt;&gt;\(\);

    for \(Map.Entry&lt;String, Class&lt;?&gt;&gt; entry : labelToSubtype.entrySet\(\)\) {

        TypeAdapter&lt;?&gt; delegate = gson.getDelegateAdapter\(this, TypeToken.get\(entry.getValue\(\)\)\);

        labelToDelegate.put\(entry.getKey\(\), delegate\);

        subtypeToDelegate.put\(entry.getValue\(\), delegate\);

    }



    return new TypeAdapter&lt;R&gt;\(\) {

        @Override public R read\(JsonReader in\) throws IOException {

            JsonElement jsonElement = Streams.parse\(in\);

            JsonElement labelJsonElement = jsonElement.getAsJsonObject\(\).remove\(typeFieldName\);

            if \(labelJsonElement == null\) {

                throw new JsonParseException\("cannot deserialize " + baseType

                        + " because it does not define a field named " + typeFieldName\);

            }

            String label = labelJsonElement.getAsString\(\);

            @SuppressWarnings\("unchecked"\) // registration requires that subtype extends T

                    TypeAdapter&lt;R&gt; delegate = \(TypeAdapter&lt;R&gt;\) labelToDelegate.get\(label\);

            if \(delegate == null\) {

                throw new JsonParseException\("cannot deserialize " + baseType + " subtype named "

                        + label + "; did you forget to register a subtype?"\);

            }

            return delegate.fromJsonTree\(jsonElement\);

        }



        @Override public void write\(JsonWriter out, R value\) throws IOException {

            Class&lt;?&gt; srcType = value.getClass\(\);

            String label = subtypeToLabel.get\(srcType\);

            @SuppressWarnings\("unchecked"\) // registration requires that subtype extends T

                    TypeAdapter&lt;R&gt; delegate = \(TypeAdapter&lt;R&gt;\) subtypeToDelegate.get\(srcType\);

            if \(delegate == null\) {

                throw new JsonParseException\("cannot serialize " + srcType.getName\(\)

                        + "; did you forget to register a subtype?"\);

            }

            JsonObject jsonObject = delegate.toJsonTree\(value\).getAsJsonObject\(\);

            if \(jsonObject.has\(typeFieldName\)\) {

                throw new JsonParseException\("cannot serialize " + srcType.getName\(\)

                        + " because it already defines a field named " + typeFieldName\);

            }

            JsonObject clone = new JsonObject\(\);

            clone.add\(typeFieldName, new JsonPrimitive\(label\)\);

            for \(Map.Entry&lt;String, JsonElement&gt; e : jsonObject.entrySet\(\)\) {

                clone.add\(e.getKey\(\), e.getValue\(\)\);

            }

            Streams.write\(clone, out\);

        }

    }.nullSafe\(\);

}

}

results matching ""

    No results matching ""