Gson序列化一个多态对象列表
我试图序列化/反序列化一个涉及多态的对象,使用Gson到JSON。
这是我的序列化代码:
ObixBaseObj lobbyObj = new ObixBaseObj(); lobbyObj.setIs("obix:Lobby"); ObixOp batchOp = new ObixOp(); batchOp.setName("batch"); batchOp.setIn("obix:BatchIn"); batchOp.setOut("obix:BatchOut"); lobbyObj.addChild(batchOp); Gson gson = new Gson(); System.out.println(gson.toJson(lobbyObj));
结果如下:
{"obix":"obj","is":"obix:Lobby","children":[{"obix":"op","name":"batch"}]}
序列化主要工作,除了遗漏了inheritance成员的内容(特别是obix:BatchIn
和obixBatchout
string丢失)。 这是我的基类:
public class ObixBaseObj { protected String obix; private String display; private String displayName; private ArrayList<ObixBaseObj> children; public ObixBaseObj() { obix = "obj"; } public void setName(String name) { this.name = name; } ... }
这是我的inheritance类(ObixOp)的样子:
public class ObixOp extends ObixBaseObj { private String in; private String out; public ObixOp() { obix = "op"; } public ObixOp(String in, String out) { obix = "op"; this.in = in; this.out = out; } public String getIn() { return in; } public void setIn(String in) { this.in = in; } public String getOut() { return out; } public void setOut(String out) { this.out = out; } }
我意识到我可以使用适配器,但问题是,我正在序列化基类typesObixBaseObj
。 大约有25个类从这个inheritance。 我怎样才能使这项工作优雅?
我认为一个自定义的串行器/解串器是唯一的方法来进行,我试图build议你最紧凑的方式来实现它,我已经find了。 我很抱歉没有使用你的类,但想法是一样的(我只想要至less1个基类和2个扩展类)。
BaseClass.java
public class BaseClass{ @Override public String toString() { return "BaseClass [list=" + list + ", isA=" + isA + ", x=" + x + "]"; } public ArrayList<BaseClass> list = new ArrayList<BaseClass>(); protected String isA="BaseClass"; public int x; }
ExtendedClass1.java
public class ExtendedClass1 extends BaseClass{ @Override public String toString() { return "ExtendedClass1 [total=" + total + ", number=" + number + ", list=" + list + ", isA=" + isA + ", x=" + x + "]"; } public ExtendedClass1(){ isA = "ExtendedClass1"; } public Long total; public Long number; }
ExtendedClass2.java
public class ExtendedClass2 extends BaseClass{ @Override public String toString() { return "ExtendedClass2 [total=" + total + ", list=" + list + ", isA=" + isA + ", x=" + x + "]"; } public ExtendedClass2(){ isA = "ExtendedClass2"; } public Long total; }
CustomDeserializer.java
public class CustomDeserializer implements JsonDeserializer<List<BaseClass>> { private static Map<String, Class> map = new TreeMap<String, Class>(); static { map.put("BaseClass", BaseClass.class); map.put("ExtendedClass1", ExtendedClass1.class); map.put("ExtendedClass2", ExtendedClass2.class); } public List<BaseClass> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { List list = new ArrayList<BaseClass>(); JsonArray ja = json.getAsJsonArray(); for (JsonElement je : ja) { String type = je.getAsJsonObject().get("isA").getAsString(); Class c = map.get(type); if (c == null) throw new RuntimeException("Unknow class: " + type); list.add(context.deserialize(je, c)); } return list; } }
CustomSerializer.java
public class CustomSerializer implements JsonSerializer<ArrayList<BaseClass>> { private static Map<String, Class> map = new TreeMap<String, Class>(); static { map.put("BaseClass", BaseClass.class); map.put("ExtendedClass1", ExtendedClass1.class); map.put("ExtendedClass2", ExtendedClass2.class); } @Override public JsonElement serialize(ArrayList<BaseClass> src, Type typeOfSrc, JsonSerializationContext context) { if (src == null) return null; else { JsonArray ja = new JsonArray(); for (BaseClass bc : src) { Class c = map.get(bc.isA); if (c == null) throw new RuntimeException("Unknow class: " + bc.isA); ja.add(context.serialize(bc, c)); } return ja; } } }
现在这是我执行的代码来testing整个事情:
public static void main(String[] args) { BaseClass c1 = new BaseClass(); ExtendedClass1 e1 = new ExtendedClass1(); e1.total = 100L; e1.number = 5L; ExtendedClass2 e2 = new ExtendedClass2(); e2.total = 200L; e2.x = 5; BaseClass c2 = new BaseClass(); c1.list.add(e1); c1.list.add(e2); c1.list.add(c2); List<BaseClass> al = new ArrayList<BaseClass>(); // this is the instance of BaseClass before serialization System.out.println(c1); GsonBuilder gb = new GsonBuilder(); gb.registerTypeAdapter(al.getClass(), new CustomDeserializer()); gb.registerTypeAdapter(al.getClass(), new CustomSerializer()); Gson gson = gb.create(); String json = gson.toJson(c1); // this is the corresponding json System.out.println(json); BaseClass newC1 = gson.fromJson(json, BaseClass.class); System.out.println(newC1); }
这是我的执行:
BaseClass [list=[ExtendedClass1 [total=100, number=5, list=[], isA=ExtendedClass1, x=0], ExtendedClass2 [total=200, list=[], isA=ExtendedClass2, x=5], BaseClass [list=[], isA=BaseClass, x=0]], isA=BaseClass, x=0] {"list":[{"total":100,"number":5,"list":[],"isA":"ExtendedClass1","x":0},{"total":200,"list":[],"isA":"ExtendedClass2","x":5},{"list":[],"isA":"BaseClass","x":0}],"isA":"BaseClass","x":0} BaseClass [list=[ExtendedClass1 [total=100, number=5, list=[], isA=ExtendedClass1, x=0], ExtendedClass2 [total=200, list=[], isA=ExtendedClass2, x=5], BaseClass [list=[], isA=BaseClass, x=0]], isA=BaseClass, x=0]
一些解释:这个技巧是由串行器/解串器内的另一个Gson完成的。 我只用isA
领域来发现正确的课程。 为了更快,我使用一个映射关联isA
string到相应的类。 然后,我使用第二个Gson对象进行正确的序列化/反序列化。 我把它声明为静态的,所以你不会使用Gson的多重分配来减慢序列化/反序列化。
Pro你实际上不会编写比这更多的代码,你让Gson做所有的工作。 你只需要记住把一个新的子类放到地图中(这个例外会让你想起那个)。
缺点你有两张地图。 我认为我的实现可以稍微改进一点,以避免地图重复,但是我把它们留给了你(或者将来的编辑,如果有的话)。
也许你想要TypeAdapter
序列化和反序列化到一个独特的对象,你应该检查TypeAdapter
类或实验一个对象,实现这两个接口。
有一个简单的解决scheme: Gson的RuntimeTypeAdapterFactory 。 你不必编写任何序列化程序,这个类就可以为你工作。 试试这个与你的代码:
ObixBaseObj lobbyObj = new ObixBaseObj(); lobbyObj.setIs("obix:Lobby"); ObixOp batchOp = new ObixOp(); batchOp.setName("batch"); batchOp.setIn("obix:BatchIn"); batchOp.setOut("obix:BatchOut"); lobbyObj.addChild(batchOp); RuntimeTypeAdapterFactory<ObixBaseObj> adapter = RuntimeTypeAdapterFactory .of(ObixBaseObj.class) .registerSubtype(ObixBaseObj.class) .registerSubtype(ObixOp.class); Gson gson2=new GsonBuilder().setPrettyPrinting().registerTypeAdapterFactory(adapter).create(); Gson gson = new Gson(); System.out.println(gson.toJson(lobbyObj)); System.out.println("---------------------"); System.out.println(gson2.toJson(lobbyObj)); }
输出:
{"obix":"obj","is":"obix:Lobby","children":[{"obix":"op","name":"batch","children":[]}]} --------------------- { "type": "ObixBaseObj", "obix": "obj", "is": "obix:Lobby", "children": [ { "type": "ObixOp", "in": "obix:BatchIn", "out": "obix:BatchOut", "obix": "op", "name": "batch", "children": [] } ] }
编辑:更好的工作示例。
你说从ObixBaseObj
inheritance了大约25个类。
我们开始写一个新的类,GsonUtils
public class GsonUtils { private static final GsonBuilder gsonBuilder = new GsonBuilder() .setPrettyPrinting(); public static void registerType( RuntimeTypeAdapterFactory<?> adapter) { gsonBuilder.registerTypeAdapterFactory(adapter); } public static Gson getGson() { return gsonBuilder.create(); }
每次我们需要一个Gson
对象,而不是调用new Gson()
,我们会调用
GsonUtils.getGson()
我们将这个代码添加到ObixBaseObj中:
public class ObixBaseObj { protected String obix; private String display; private String displayName; private String name; private String is; private ArrayList<ObixBaseObj> children = new ArrayList<ObixBaseObj>(); // new code private static final RuntimeTypeAdapterFactory<ObixBaseObj> adapter = RuntimeTypeAdapterFactory.of(ObixBaseObj.class); private static final HashSet<Class<?>> registeredClasses= new HashSet<Class<?>>(); static { GsonUtils.registerType(adapter); } private synchronized void registerClass() { if (!registeredClasses.contains(this.getClass())) { registeredClasses.add(this.getClass()); adapter.registerSubtype(this.getClass()); } } public ObixBaseObj() { registerClass(); obix = "obj"; }
为什么? 因为每次这个类或ObixBaseObj
一个子类被实例化时,它将在RuntimeTypeAdapter
注册的类
在儿童class,只需要很小的变化:
public class ObixOp extends ObixBaseObj { private String in; private String out; public ObixOp() { super(); obix = "op"; } public ObixOp(String in, String out) { super(); obix = "op"; this.in = in; this.out = out; }
工作示例:
public static void main(String[] args) { ObixBaseObj lobbyObj = new ObixBaseObj(); lobbyObj.setIs("obix:Lobby"); ObixOp batchOp = new ObixOp(); batchOp.setName("batch"); batchOp.setIn("obix:BatchIn"); batchOp.setOut("obix:BatchOut"); lobbyObj.addChild(batchOp); Gson gson = GsonUtils.getGson(); System.out.println(gson.toJson(lobbyObj)); }
输出:
{ "type": "ObixBaseObj", "obix": "obj", "is": "obix:Lobby", "children": [ { "type": "ObixOp", "in": "obix:BatchIn", "out": "obix:BatchOut", "obix": "op", "name": "batch", "children": [] } ] }
我希望它有帮助。
我赞赏这里的其他答案,导致我在解决这个问题的道路上。 我使用了RuntimeTypeAdapterFactory
和Reflection的组合。
我还创build了一个辅助类,以确保使用正确configuration的Gson。
在GsonHelper类的一个静态块中,我有以下代码通过我的项目来查找并注册所有适当的types。 所有将通过JSON-ification的对象都是Jsonable的子types。 您将需要更改以下内容:
-
Reflections
my.project应该是你的包名。 -
Jsonable.class
是我的基类。 replace你的。 -
我喜欢让该字段显示完整的规范名称,但显然如果您不想/需要它,则可以省略该部分的调用来注册该子types。
RuntimeAdapterFactory
className
也是一样的; 我有数据项目已经使用type
字段。private static final GsonBuilder gsonBuilder = new GsonBuilder() .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") .excludeFieldsWithoutExposeAnnotation() .setPrettyPrinting(); static { Reflections reflections = new Reflections("my.project"); Set<Class<? extends Jsonable>> allTypes = reflections.getSubTypesOf(Jsonable.class); for (Class< ? extends Jsonable> serClass : allTypes){ Set<?> subTypes = reflections.getSubTypesOf(serClass); if (subTypes.size() > 0){ RuntimeTypeAdapterFactory<?> adapterFactory = RuntimeTypeAdapterFactory.of(serClass, "className"); for (Object o : subTypes ){ Class c = (Class)o; adapterFactory.registerSubtype(c, c.getCanonicalName()); } gsonBuilder.registerTypeAdapterFactory(adapterFactory); } } } public static Gson getGson() { return gsonBuilder.create(); }