1、简述
> 番外篇 对于多态对象的解析 需要引用 使用 RuntimeTypeAdapterFactory 类解决多态问题,但是这个类需要单独下载, githut地址 RuntimeTypeAdapterFactory.java 1、需要将这个 RuntimeTypeAdapterFactory 类复制到项目中去, 2、反序列化json 还需要 添加额外(或已知)的 一个唯一属性 3、进行相应规则的注册(如下 测试)
2、基本类
运行时绑定类适配器工厂
package sun.rain.amazing.gson.extra; 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; public final class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory { private final Class<?> baseType; private final String typeFieldName; private final Map<String, Class<?>> labelToSubtype = new LinkedHashMap<String, Class<?>>(); private final Map<Class<?>, String> subtypeToLabel = new LinkedHashMap<Class<?>, String>(); private RuntimeTypeAdapterFactory(Class<?> baseType, String typeFieldName) { if (typeFieldName == null || baseType == null) { throw new NullPointerException(); } this.baseType = baseType; this.typeFieldName = typeFieldName; } public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName) { return new RuntimeTypeAdapterFactory<T>(baseType, typeFieldName); } public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType) { return new RuntimeTypeAdapterFactory<T>(baseType, "type"); } public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> 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; } public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) { return registerSubtype(type, type.getSimpleName()); } public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) { if (type.getRawType() != baseType) { return null; } final Map<String, TypeAdapter<?>> labelToDelegate = new LinkedHashMap<String, TypeAdapter<?>>(); final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate = new LinkedHashMap<Class<?>, TypeAdapter<?>>(); for (Map.Entry<String, Class<?>> 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<R>() { @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<R> delegate = (TypeAdapter<R>) 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 = subtypeToLabel.get(srcType); @SuppressWarnings("unchecked") // registration requires that subtype extends T TypeAdapter<R> delegate = (TypeAdapter<R>) 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<String, JsonElement> e : jsonObject.entrySet()) { clone.add(e.getKey(), e.getValue()); } Streams.write(clone, out); } }.nullSafe(); } }
继承类
/** * @author sunRainAmazing */ @Data @AllArgsConstructor @NoArgsConstructor public abstract class BaseInfo { private String type1; private String ssl; }
@Data @NoArgsConstructor public class GsonInfo extends BaseInfo { private Integer idx; private String gson; public GsonInfo(String type, String ssl, Integer idx, String gson) { super(type, ssl); this.idx = idx; this.gson = gson; } public GsonInfo(Integer idx, String gson) { this.idx = idx; this.gson = gson; } }
@Data @NoArgsConstructor public class UserInfo extends BaseInfo { private Integer id; private String name; public UserInfo(String type, String ssl, Integer id, String name) { super(type, ssl); this.id = id; this.name = name; } public UserInfo(Integer id, String name) { this.id = id; this.name = name; } }
@Data @AllArgsConstructor @NoArgsConstructor public class GsonObj implements IGson { private BaseInfo one; private BaseInfo two; }
接口类
public interface IGson {
}
@Data @AllArgsConstructor @NoArgsConstructor public class GsonOne implements IGson { private Integer idOne; private String uuid; }
@Data @AllArgsConstructor @NoArgsConstructor public class GsonTwo implements IGson { private Integer idTwo; private String name; }
@Data @AllArgsConstructor @NoArgsConstructor public class Result { private String name; private IGson iGson1; private IGson iGson2; }
3、测试类
关于继承的测试
package sun.rain.amazing.gson.ex; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import org.junit.Test; import sun.rain.amazing.gson.extra.BaseInfo; import sun.rain.amazing.gson.extra.RuntimeTypeAdapterFactory; import sun.rain.amazing.gson.extra.extend.GsonInfo; import sun.rain.amazing.gson.extra.extend.UserInfo; import sun.rain.amazing.gson.extra.extend.GsonObj; /** * @author sunRainAmazing */ public class BaseInfoTest { private Gson gson = new Gson(); /** * {"one":{"idx":101,"gson":"120-365","type":"A","ssl":"LINE-SSL"}, * "two":{"id":102,"name":"tomcat","type":"B","ssl":"CODE-SSL"}} * * java.lang.RuntimeException: * Failed to invoke public sun.rain.amazing.gson.extra.BaseInfo() with no args */ @Deprecated @Test public void testBaseInfo(){ GsonInfo go = new GsonInfo("A","LINE-SSL",101,"120-365"); UserInfo to = new UserInfo("B","CODE-SSL",102,"tomcat"); GsonObj gos = new GsonObj(go,to); String json = gson.toJson(gos); System.out.println(json); // Failed to invoke public sun.rain.amazing.gson.extra.BaseInfo() with no args GsonObj g = gson.fromJson(json,GsonObj.class); System.out.println(g); } /** * 默认 会出错 * BaseInfo because it does not define a field named type * 未定义类型,这里需要添加一个唯一的标识符 才可以正常反序列化 -- * 见下一个测试 */ @Test public void testBaseInfo1(){ GsonInfo go = new GsonInfo("A","LINE-SSL",101,"120-365"); UserInfo to = new UserInfo("B","CODE-SSL",102,"tomcat"); GsonObj gos = new GsonObj(go,to); //格式化输出 gson = new GsonBuilder().setPrettyPrinting().create(); String json = gson.toJson(gos); System.out.println(json); RuntimeTypeAdapterFactory<BaseInfo> typeFactory = RuntimeTypeAdapterFactory //在这里,您可以指定哪个是父类,哪个字段特定于子类。 .of(BaseInfo.class) // 如果标志等于类名,则可以跳过第二个参数。 仅当“类型”字段不等于类名时,才需要这样做。 .registerSubtype(GsonInfo.class) .registerSubtype(UserInfo.class); gson = new GsonBuilder().registerTypeAdapterFactory(typeFactory).create(); GsonObj g = gson.fromJson(json,GsonObj.class); System.out.println(g); } /** 因此json 必须带 一个唯一标示 且这个标识具有唯一值 * 在 此json中 不能重复 { "one": { "idx": 101, "gson": "120-365", "type1": "A", "ssl": "LINE-SSL" }, "two": { "id": 102, "name": "tomcat", "type1": "B", "ssl": "CODE-SSL" } } 将上面的格式添加 一个 uuid 唯一标识 { "one": { "uuid": "A-one", "idx": 101, "gson": "120-365", "type1": "A", "ssl": "LINE-SSL" }, "two": { "uuid": "A-two", "id": 102, "name": "tomcat", "type1": "B", "ssl": "CODE-SSL" } } 此时 带有 uuid的 为 type 值 而 uuid所对应的值("A-one") 则对应子类的label */ // GsonObj(one=GsonInfo(idx=101, gson=120-365), two=UserInfo(id=102, name=tomcat)) // 才可以正常反序列化 @Test public void testBaseInfoRight(){ String json = "{\n" + " \"one\": {\n" + " \"uuid\": \"A-one\",\n" + " \"idx\": 101,\n" + " \"gson\": \"120-365\",\n" + " \"type1\": \"A\",\n" + " \"ssl\": \"LINE-SSL\"\n" + " },\n" + " \"two\": {\n" + " \"uuid\": \"A-two\",\n" + " \"id\": 102,\n" + " \"name\": \"tomcat\",\n" + " \"type1\": \"B\",\n" + " \"ssl\": \"CODE-SSL\"\n" + " }\n" + " }"; RuntimeTypeAdapterFactory<BaseInfo> typeFactory = RuntimeTypeAdapterFactory //在这里,您可以指定哪个是父类,哪个字段特定于子类。 // typeFieldName 的值 默认 为 type .of(BaseInfo.class,"uuid") // 如果标志等于类名,则可以跳过第二个参数。 仅当“类型”字段不等于类名时,才需要这样做。 // label值 则 对应 传入的 typeFieldName 的值 .registerSubtype(GsonInfo.class,"A-one") .registerSubtype(UserInfo.class,"A-two"); gson = new GsonBuilder().registerTypeAdapterFactory(typeFactory).create(); GsonObj g = gson.fromJson(json,GsonObj.class); System.out.println(g); } /** * 若反序列化中其本身 含有 type 属性 -- * 而 当前type 又不是唯一固定对应值 * 则必须定制一个 唯一属性 来对应 相应的片段json 才可以正确解析 */ }
对于接口的多态的测试
package sun.rain.amazing.gson.ex; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import org.junit.Test; import sun.rain.amazing.gson.extra.BaseInfo; import sun.rain.amazing.gson.extra.IGson; import sun.rain.amazing.gson.extra.RuntimeTypeAdapterFactory; import sun.rain.amazing.gson.extra.extend.GsonInfo; import sun.rain.amazing.gson.extra.extend.GsonObj; import sun.rain.amazing.gson.extra.extend.UserInfo; import sun.rain.amazing.gson.extra.impl.GsonOne; import sun.rain.amazing.gson.extra.impl.GsonTwo; import sun.rain.amazing.gson.extra.impl.Result; import java.util.ArrayList; import java.util.List; /** * @author sunRainAmazing */ public class ImplGsonTest { private Gson gson = new Gson(); /** * {"name":"Kent","iGsonList":[{"idOne":101,"uuid":"120-365"}, * {"idTwo":102,"name":"tomcat"}]} * * java.lang.RuntimeException: * Unable to invoke no-args constructor for interface * sun.rain.amazing.gson.extra.IGson. * * 可以被序列化 --- 但是 不能被反序列化 */ @Deprecated @Test public void testGsonImpl(){ IGson go = new GsonOne(101,"120-365"); IGson to = new GsonTwo(102,"tomcat"); Result gos = new Result("Kent",go,to); String json = gson.toJson(gos); System.out.println(json); // Failed to invoke public sun.rain.amazing.gson.extra.BaseInfo() with no args Result g = gson.fromJson(json,Result.class); System.out.println(g); } /** {"name":"Kent", "iGson1":{ "idOne":101,"uuid":"120-365"}, "iGson2":{ "idTwo":102,"name":"tomcat"}} -- 改成 {"name":"Kent", "iGson1":{ "type":"A", "idOne":101,"uuid":"120-365"}, "iGson2":{ "type":"B", "idTwo":102,"name":"tomcat"}} ========================================== GsonTwo(idTwo=102, name=tomcat) Result(name=Kent, iGson1=GsonOne(idOne=101, uuid=120-365), iGson2=GsonTwo(idTwo=102, name=tomcat)) */ @Test public void testGsonImpl1(){ String json = "{\"name\":\"Kent\",\n" + " \"iGson1\":{\n" + " \"type\":\"A\",\n" + " \"idOne\":101,\"uuid\":\"120-365\"},\n" + " \"iGson2\":{\n" + " \"type\":\"B\",\n" + " \"idTwo\":102,\"name\":\"tomcat\"}}"; RuntimeTypeAdapterFactory<IGson> typeFactory = RuntimeTypeAdapterFactory //在这里,您可以指定哪个是父类,哪个字段特定于子类。 // typeFieldName 的值 默认 为 type -- 采用默认的type .of(IGson.class) // 如果标志等于类名,则可以跳过第二个参数。 仅当“类型”字段不等于类名时,才需要这样做。 // label值 则 对应 传入的 typeFieldName 的值 .registerSubtype(GsonOne.class,"A") .registerSubtype(GsonTwo.class,"B"); gson = new GsonBuilder() .registerTypeAdapterFactory(typeFactory) .create(); Result g = gson.fromJson(json,Result.class); System.out.println(g.getIGson2()); System.out.println(g); } /** * 注意 不能 改成这样的json {"name":"Kent"," iGson1":{ "type":"A", "idOne":101,"uuid":"120-365"}, "iGson2":{ "type":"B", "idTwo":102,"name":"tomcat"}} */ /** * * 这是因为 json中 iGson1 的 key 多了一个 \n (换行符) 和 前置空格 * GsonTwo(idTwo=102, name=tomcat) * Result(name=Kent, iGson1=null, iGson2=GsonTwo(idTwo=102, name=tomcat)) */ @Test public void testGsonImpl2(){ String json = "{\"name\":\"Kent\",\"\n" + " iGson1\":{\n" + " \"type\":\"A\",\n" + " \"idOne\":101,\"uuid\":\"120-365\"},\n" + " \"iGson2\":{\n" + " \"type\":\"B\",\n" + " \"idTwo\":102,\"name\":\"tomcat\"}} "; RuntimeTypeAdapterFactory<IGson> typeFactory = RuntimeTypeAdapterFactory //在这里,您可以指定哪个是父类,哪个字段特定于子类。 // typeFieldName 的值 默认 为 type -- 采用默认的type .of(IGson.class) // 如果标志等于类名,则可以跳过第二个参数。 仅当“类型”字段不等于类名时,才需要这样做。 // label值 则 对应 传入的 typeFieldName 的值 .registerSubtype(GsonOne.class,"A") .registerSubtype(GsonTwo.class,"B"); gson = new GsonBuilder() .registerTypeAdapterFactory(typeFactory) .create(); Result g = gson.fromJson(json,Result.class); System.out.println(g); } }