gson – 序列化任何类型的对象时如何包含类名属性

后来意识到我需要在我的应用程序中序列化对象时将类名称包含为属性。 如果我为序列化的任何非基本对象添加了类名属性,那可能是最好的。

我看到这是Genson中使用useClassMetadata方法的内置function。 但是我已经在我的项目中使用了gson,所以如果我坚持下去就会有所帮助。

这是我目前的尝试:

 package com.mycompany.javatest; import com.google.gson.*; import java.lang.reflect.*; public class JavaTest { public static class GenericSerializer implements JsonSerializer, JsonDeserializer { private static final String CLASS_PROPERTY_NAME = "class"; @Override public JsonElement serialize(Object src, Type typeOfSrc, JsonSerializationContext context) { JsonElement retValue = context.serialize(src); if (retValue.isJsonObject()) { retValue.getAsJsonObject().addProperty(CLASS_PROPERTY_NAME, src.getClass().getName()); } return retValue; } @Override public Object deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { Class actualClass; if (json.isJsonObject()) { JsonObject jsonObject = json.getAsJsonObject(); String className = jsonObject.get(CLASS_PROPERTY_NAME).getAsString(); try { actualClass = Class.forName(className); } catch (ClassNotFoundException e) { e.printStackTrace(); throw new JsonParseException(e.getMessage()); } } else { actualClass = typeOfT.getClass(); } return context.deserialize(json, actualClass); } } public static class MyClass { private final String name = "SpongePants SquareBob"; } public static void main(String[] args) { MyClass obj = new MyClass(); GsonBuilder gb = new GsonBuilder(); gb.registerTypeAdapter(Object.class, new GenericSerializer()); Gson gson = gb.create(); System.out.println(gson.toJson(obj, Object.class)); } } 

打印

 {"name":"SpongePants SquareBob"} 

我想要它打印

 {"name":"SpongePants SquareBob","class":"com.mycompany.javatest$MyClass"} 

编辑:另一次尝试(这次使用GsonFire)

 package com.mycompany.javatest; import com.google.gson.*; import io.gsonfire.*; public class JavaTest { public static class DummyData { private final String someData = "1337"; } private static final String CLASS_PROPERTY_NAME = "class"; public static void main(String[] args) { GsonFireBuilder gfb = new GsonFireBuilder(); gfb.registerPostProcessor(Object.class, new PostProcessor() { @Override public void postDeserialize(Object t, JsonElement je, Gson gson) { // Ignore } @Override public void postSerialize(JsonElement je, Object t, Gson gson) { if (je.isJsonObject()) { je.getAsJsonObject().add(CLASS_PROPERTY_NAME, new JsonPrimitive(t.getClass().getTypeName())); } } }); gfb.registerTypeSelector(Object.class, (JsonElement je) -> { System.out.println(je); if (je.isJsonObject()) { try { return Class.forName(je.getAsJsonObject().get(CLASS_PROPERTY_NAME).getAsString()); } catch (ClassNotFoundException ex) { ex.printStackTrace(); } } return null; }); Gson gson = gfb.createGson(); DummyData dd = new DummyData(); String json = gson.toJson(dd); System.out.println(json); DummyData dd2 = (DummyData) gson.fromJson(json, Object.class); // <-- gives me a ClassCastException } } 

又一个答案。 花了一点时间。

旁注:如果您以递归方式使用reflection来计算类中的字段,则上述解决方案将起作用。 然后使用特殊的序列化器序列化那些,同时为父对象使用单独的序列化器。 这样可以避免堆栈溢出。

话虽如此 – 我是一个懒惰的开发者,所以我喜欢做懒惰的事情。 我正在为您调整谷歌解决方案。

注意:请测试这个并根据您的需要调整它。 这是一个原型,我没有清理不必要的代码或检查可能的问题>

代码的原始来源:

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

所以,这是基于RuntimeTypeAdapterFactory 。 该工厂由谷歌提供,其目的是支持分层反序列化。 为此,您将注册一个基类和所有子类,并使用您要添加为标识符的属性。 如果您阅读javadocs,这将更加清晰。

这显然为我们提供了我们想要的东西:递归地为可以处理这些的类类型注册不同的适配器,而不是在圆圈中运行并导致堆栈溢出。 有一个重要问题:您必须注册所有子类。 这显然不合适(尽管有人可能会说你可以通过类路径解析来简单地在启动时添加所有类,以便能够在任何地方使用它)。 所以我查看了源代码并更改了代码以动态执行此操作。 请注意,谷歌警告不要这样做 – 按照你自己的条件使用它:)

这是我的工厂:

 import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; 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; /** * Adapts values whose runtime type may differ from their declaration type. This * is necessary when a field's type is not the same type that GSON should create * when deserializing that field. For example, consider these types: * 
 {@code * abstract class Shape { * int x; * int y; * } * class Circle extends Shape { * int radius; * } * class Rectangle extends Shape { * int width; * int height; * } * class Diamond extends Shape { * int width; * int height; * } * class Drawing { * Shape bottomShape; * Shape topShape; * } * }

*

Without additional type information, the serialized JSON is ambiguous. Is * the bottom shape in this drawing a rectangle or a diamond?

 {@code * { * "bottomShape": { * "width": 10, * "height": 5, * "x": 0, * "y": 0 * }, * "topShape": { * "radius": 2, * "x": 4, * "y": 1 * } * }}

* This class addresses this problem by adding type information to the * serialized JSON and honoring that type information when the JSON is * deserialized:

 {@code * { * "bottomShape": { * "type": "Diamond", * "width": 10, * "height": 5, * "x": 0, * "y": 0 * }, * "topShape": { * "type": "Circle", * "radius": 2, * "x": 4, * "y": 1 * } * }}

* Both the type field name ({@code "type"}) and the type labels ({@code * "Rectangle"}) are configurable. * *

Registering Types

* Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field * name to the {@link #of} factory method. If you don't supply an explicit type * field name, {@code "type"} will be used.

 {@code * RuntimeTypeAdapterFactory shapeAdapterFactory * = RuntimeTypeAdapterFactory.of(Shape.class, "type"); * }

* Next register all of your subtypes. Every subtype must be explicitly * registered. This protects your application from injection attacks. If you * don't supply an explicit type label, the type's simple name will be used. *

 {@code * shapeAdapter.registerSubtype(Rectangle.class, "Rectangle"); * shapeAdapter.registerSubtype(Circle.class, "Circle"); * shapeAdapter.registerSubtype(Diamond.class, "Diamond"); * }

* Finally, register the type adapter factory in your application's GSON builder: *

 {@code * Gson gson = new GsonBuilder() * .registerTypeAdapterFactory(shapeAdapterFactory) * .create(); * }

* Like {@code GsonBuilder}, this API supports chaining:

 {@code * RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class) * .registerSubtype(Rectangle.class) * .registerSubtype(Circle.class) * .registerSubtype(Diamond.class); * }

*/ public final class RuntimeClassNameTypeAdapterFactory implements TypeAdapterFactory { private final Class baseType; private final String typeFieldName; private final Map> labelToSubtype = new LinkedHashMap>(); private final Map, String> subtypeToLabel = new LinkedHashMap, String>(); private RuntimeClassNameTypeAdapterFactory(Class 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 RuntimeClassNameTypeAdapterFactory of(Class baseType, String typeFieldName) { return new RuntimeClassNameTypeAdapterFactory(baseType, typeFieldName); } /** * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as * the type field name. */ public static RuntimeClassNameTypeAdapterFactory of(Class baseType) { return new RuntimeClassNameTypeAdapterFactory(baseType, "class"); } /** * 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 RuntimeClassNameTypeAdapterFactory registerSubtype(Class 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 RuntimeClassNameTypeAdapterFactory registerSubtype(Class type) { return registerSubtype(type, type.getSimpleName()); } public TypeAdapter create(Gson gson, TypeToken type) { final Map> labelToDelegate = new LinkedHashMap>(); final Map, TypeAdapter> subtypeToDelegate = new LinkedHashMap, TypeAdapter>(); // && !String.class.isAssignableFrom(type.getRawType()) if(Object.class.isAssignableFrom(type.getRawType()) ) { TypeAdapter delegate = gson.getDelegateAdapter(this, type); labelToDelegate.put("class", delegate); subtypeToDelegate.put(type.getRawType(), delegate); } // for (Map.Entry> entry : labelToSubtype.entrySet()) { // TypeAdapter delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue())); // labelToDelegate.put(entry.getKey(), delegate); // subtypeToDelegate.put(entry.getValue(), delegate); // } return new TypeAdapter() { @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 delegate = (TypeAdapter) 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 srcType = value.getClass(); String label = srcType.getName(); @SuppressWarnings("unchecked") // registration requires that subtype extends T TypeAdapter delegate = (TypeAdapter) subtypeToDelegate.get(srcType); if (delegate == null) { throw new JsonParseException("cannot serialize " + srcType.getName() + "; did you forget to register a subtype?"); } JsonElement jsonTree = delegate.toJsonTree(value); if(jsonTree.isJsonPrimitive()) { Streams.write(jsonTree, out); } else { JsonObject jsonObject = jsonTree.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 e : jsonObject.entrySet()) { clone.add(e.getKey(), e.getValue()); } Streams.write(clone, out); } } }.nullSafe(); } }

我为你添加了所有import商品。 这不是(真的)在maven中心发布的,虽然你可以在这里找到它: https : //mvnrepository.com/artifact/org.danilopianini/gson-extras/0.1.0

无论你是否需要进行调整以使其适合你,所以我复制了一份。 该副本完全编译,您只需将其粘贴到您的代码中,并为自己节省额外的依赖。

这段代码的重要部分如下:(我故意将它们留在但注释掉了,你可以告诉)

create(Gson gson, TypeToken type)

检查原始类型是否可从String类中分配。 您希望将此应用于每个类对象,因此这将解决此问题。 注意如果类型是在类中注册的话,之前的代码会查找 – 不再需要(因此不需要变量;你应该清理代码)

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

首先,我们摆脱了标签。 我们的标签始终是源类型的名称。 这是在:

String label = srcType.getName();

其次,我们必须区分原始类型和对象类型。 原始类型是Gson世界中的字符串,整数等。 这意味着我们上面的检查(添加适配器)并没有发现这些Object类型是契约基元类型的事实。 所以我们这样做:

 if(jsonTree.isJsonPrimitive()) { Streams.write(jsonTree, out); 

这样可以解决这个问题。 如果它是原始的,只需将树写入流中。 如果不是,我们将所有其他字段类字段写入其中。

 JsonObject clone = new JsonObject(); clone.add(typeFieldName, new JsonPrimitive(label)); for (Map.Entry e : jsonObject.entrySet()) { clone.add(e.getKey(), e.getValue()); } Streams.write(clone, out); 

Fewww – 终于现在可以解决这个问题了。 以下是certificate我的代码执行(我相信)您希望它执行的示例;)

 public class GsonClassNameTest { static Gson create = new GsonBuilder().registerTypeAdapterFactory(RuntimeClassNameTypeAdapterFactory.of(Object.class)).create(); public static void main(String[] args) { String json = create.toJson(new X()); System.out.println(json); } public static class X { public String test = "asd"; public int xyz = 23; public Y y_class = new Y(); } public static class Y { String yTest = "asd2"; Z zTest = new Z(); } public static class Z { long longVal = 25; double doubleTest = 2.4; } } 

这现在为你输出这个json:

 { "class":"google.GsonClassNameTest$X", "test":"asd", "xyz":23, "y_class":{ "class":"google.GsonClassNameTest$Y", "yTest":"asd2", "zTest":{ "class":"google.GsonClassNameTest$Z", "longVal":25, "doubleTest":2.4 } } } 

如您所见,正确创建了字符串,长整数,整数。 每个类对象recursivley也得到了它的类名。

这是一种通用方法,应该适用于您创建的所有内容。 但是,如果你决定接受这个,请帮我一个忙,并写一些unit testing;)就像我之前提到的那样,我将这个实现原型化。

希望能让我得到一个嘀嗒:)

问候,

阿图尔

接受@ padaadb的答案,但只是想粘贴我正在使用的代码。 它负责使用类型进行序列化并反序列化为正确的子类:

 package com.mycompany.javatest; import com.google.gson.*; import java.lang.reflect.*; import org.junit.*; public class JavaTest { public static class GenericSerializer implements JsonSerializer, JsonDeserializer { private static final String CLASS_PROPERTY_NAME = "class"; private final Gson gson; public GenericSerializer() { gson = new Gson(); } public GenericSerializer(Gson gson) { this.gson = gson; } @Override public Object deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { Class actualClass; if (json.isJsonObject()) { JsonObject jsonObject = json.getAsJsonObject(); String className = jsonObject.get(CLASS_PROPERTY_NAME).getAsString(); try { actualClass = Class.forName(className); } catch (ClassNotFoundException e) { e.printStackTrace(); throw new JsonParseException(e.getMessage()); } } else { actualClass = typeOfT.getClass(); } return gson.fromJson(json, actualClass); } @Override public JsonElement serialize(Object src, Type typeOfSrc, JsonSerializationContext context) { JsonElement retValue = gson.toJsonTree(src); if (retValue.isJsonObject()) { retValue.getAsJsonObject().addProperty(CLASS_PROPERTY_NAME, src.getClass().getName()); } return retValue; } } public static void main(String[] args) { GsonBuilder builder = new GsonBuilder(); builder.registerTypeHierarchyAdapter(Object.class, new GenericSerializer()); Gson gson = builder.create(); SomeSuperClass x = new SomeSubClass(); String json = gson.toJson(x); SomeSuperClass y = gson.fromJson(json, SomeSuperClass.class); // Usually, y would now be of type SomeSuperClass Assert.assertEquals(x.getClass(), y.getClass()); // y is actually of type SomeSubClass (!) System.out.println("y.getClass()= " + y.getClass()); } public static class SomeSuperClass { } public static class SomeSubClass extends SomeSuperClass { private final String someMember = "12345"; } } 

我自己尝试了这个似乎工作:

 public class GsonClassNameTest { public static void main(String[] args) { Gson create = new GsonBuilder().registerTypeHierarchyAdapter(Object.class, new ODeserialiser()).create(); String json = create.toJson(new X()); System.out.println(json); } public static class ODeserialiser implements JsonSerializer { @Override public JsonElement serialize(Object src, Type typeOfSrc, JsonSerializationContext context) { Gson gson = new Gson(); JsonElement serialize = gson.toJsonTree(src); JsonObject o = (JsonObject) serialize; o.addProperty("class", src.getClass().getName()); return serialize; } } public static class X { public String test = "asd"; } } 

这打印:

 {"test":"asd","class":"google.GsonClassNameTest$X"} 

细节:

您必须注册一个层次结构适配器,这样如果您使用Object类注册它,它将被调用您传递给它的任何类型。

您还必须在自定义序列化程序中使用不同的Gson实例,否则您只是继续在圈子中运行并获得Stackoverflow。

除此之外,非常直接:)

注意:我对gson的经验相当少,所以可能有一个更酷的解决方案。

问候,

阿图尔

pandaadb的惊人答案并不完全适合我,因为它不处理数组/列表,并且反序列化存在问题,所以我做了一些改动:

 package org.ctbto.osi.fieldapp.util.gson; 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; /** * 

* Disclaimer: taken from here https://stackoverflow.com/a/40133286/285091 with some modifications *

* * Adapts values whose runtime type may differ from their declaration type. This * is necessary when a field's type is not the same type that GSON should create * when deserializing that field. For example, consider these types: *
 {@code * abstract class Shape { * int x; * int y; * } * class Circle extends Shape { * int radius; * } * class Rectangle extends Shape { * int width; * int height; * } * class Diamond extends Shape { * int width; * int height; * } * class Drawing { * Shape bottomShape; * Shape topShape; * } * }

*

Without additional type information, the serialized JSON is ambiguous. Is * the bottom shape in this drawing a rectangle or a diamond?

 {@code * { * "bottomShape": { * "width": 10, * "height": 5, * "x": 0, * "y": 0 * }, * "topShape": { * "radius": 2, * "x": 4, * "y": 1 * } * }}

* This class addresses this problem by adding type information to the * serialized JSON and honoring that type information when the JSON is * deserialized:

 {@code * { * "bottomShape": { * "type": "Diamond", * "width": 10, * "height": 5, * "x": 0, * "y": 0 * }, * "topShape": { * "type": "Circle", * "radius": 2, * "x": 4, * "y": 1 * } * }}

* Both the type field name ({@code "type"}) and the type labels ({@code * "Rectangle"}) are configurable. *

*

Registering Types

* Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field * name to the {@link #of} factory method. If you don't supply an explicit type * field name, {@code "type"} will be used.

 {@code * RuntimeTypeAdapterFactory shapeAdapterFactory * = RuntimeTypeAdapterFactory.of(Shape.class, "type"); * }

* Next register all of your subtypes. Every subtype must be explicitly * registered. This protects your application from injection attacks. If you * don't supply an explicit type label, the type's simple name will be used. *

 {@code * shapeAdapter.registerSubtype(Rectangle.class, "Rectangle"); * shapeAdapter.registerSubtype(Circle.class, "Circle"); * shapeAdapter.registerSubtype(Diamond.class, "Diamond"); * }

* Finally, register the type adapter factory in your application's GSON builder: *

 {@code * Gson gson = new GsonBuilder() * .registerTypeAdapterFactory(shapeAdapterFactory) * .create(); * }

* Like {@code GsonBuilder}, this API supports chaining:

 {@code * RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class) * .registerSubtype(Rectangle.class) * .registerSubtype(Circle.class) * .registerSubtype(Diamond.class); * }

*/ public final class RuntimeClassNameTypeAdapterFactory implements TypeAdapterFactory { private final Class baseType; private final String typeFieldName; private final Map> labelToSubtype = new LinkedHashMap>(); private final Map, String> subtypeToLabel = new LinkedHashMap, String>(); private RuntimeClassNameTypeAdapterFactory(Class 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 RuntimeClassNameTypeAdapterFactory of(Class baseType, String typeFieldName) { return new RuntimeClassNameTypeAdapterFactory(baseType, typeFieldName); } /** * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as * the type field name. */ public static RuntimeClassNameTypeAdapterFactory of(Class baseType) { return new RuntimeClassNameTypeAdapterFactory(baseType, "class"); } /** * 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 RuntimeClassNameTypeAdapterFactory registerSubtype(Class 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 RuntimeClassNameTypeAdapterFactory registerSubtype(Class type) { return registerSubtype(type, type.getSimpleName()); } public TypeAdapter create(Gson gson, TypeToken type) { final Map> labelToDelegate = new LinkedHashMap>(); final Map, TypeAdapter> subtypeToDelegate = new LinkedHashMap, TypeAdapter>(); // && !String.class.isAssignableFrom(type.getRawType()) if (Object.class.isAssignableFrom(type.getRawType())) { TypeAdapter delegate = gson.getDelegateAdapter(this, type); labelToDelegate.put(type.getRawType().getName(), delegate); subtypeToDelegate.put(type.getRawType(), delegate); } // for (Map.Entry> entry : labelToSubtype.entrySet()) { // TypeAdapter delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue())); // labelToDelegate.put(entry.getKey(), delegate); // subtypeToDelegate.put(entry.getValue(), delegate); // } return new TypeAdapter() { @SuppressWarnings("unchecked") @Override public R read(JsonReader in) throws IOException { JsonElement jsonElement = Streams.parse(in); if (jsonElement.isJsonObject()) { 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(); TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label); if (delegate == null) { Class aClass; try { aClass = (Class) Class.forName(label); } catch (ClassNotFoundException e) { throw new JsonParseException("Cannot find class " + label, e); } TypeToken subClass = TypeToken.get(aClass); delegate = gson.getDelegateAdapter(RuntimeClassNameTypeAdapterFactory.this, subClass); if (delegate == null) { throw new JsonParseException("cannot deserialize " + baseType + " subtype named " + label + "; did you forget to register a subtype?"); } } return delegate.fromJsonTree(jsonElement); } else if (jsonElement.isJsonNull()) { return null; } else { TypeAdapter delegate = gson.getDelegateAdapter(RuntimeClassNameTypeAdapterFactory.this, type); if (delegate == null) { throw new JsonParseException("cannot deserialize " + baseType + "; did you forget to register a subtype?"); } return delegate.fromJsonTree(jsonElement); } } @Override public void write(JsonWriter out, R value) throws IOException { Class srcType = value.getClass(); String label = srcType.getName(); TypeAdapter delegate = getDelegate(srcType); if (delegate == null) { throw new JsonParseException("cannot serialize " + srcType.getName() + "; did you forget to register a subtype?"); } JsonElement jsonTree = delegate.toJsonTree(value); if (!jsonTree.isJsonObject()) { Streams.write(jsonTree, out); } else { JsonObject jsonObject = jsonTree.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 e : jsonObject.entrySet()) { clone.add(e.getKey(), e.getValue()); } Streams.write(clone, out); } } @SuppressWarnings("unchecked") private TypeAdapter getDelegate(Class srcType) { TypeAdapter typeAdapter = subtypeToDelegate.get(srcType); if (typeAdapter != null) { return (TypeAdapter) typeAdapter; } for (Map.Entry, TypeAdapter> classTypeAdapterEntry : subtypeToDelegate.entrySet()) { if (classTypeAdapterEntry.getKey().isAssignableFrom(srcType)) { return (TypeAdapter) classTypeAdapterEntry.getValue(); } } return null; } }.nullSafe(); } }

尽管如此,所有的功劳仍归他/她所有。 如他/她所说,请在使用之前测试此代码!