Java Gson多态序列化
使用Gson 2.2.2,我试图序列化POJO(行为)的数组列表 我有一个适配器,它几乎是我在网上看到的内容的副本:Java Gson多态序列化,java,gson,Java,Gson,使用Gson 2.2.2,我试图序列化POJO(行为)的数组列表 我有一个适配器,它几乎是我在网上看到的内容的副本: public class BehaviorAdapter implements JsonSerializer<Behavior> { private static final String CLASSNAME = "CLASSNAME"; private static final String INSTANCE = "INSTANCE";
public class BehaviorAdapter implements JsonSerializer<Behavior> {
private static final String CLASSNAME = "CLASSNAME";
private static final String INSTANCE = "INSTANCE";
@Override
public JsonElement serialize(Behavior src, Type typeOfSrc,
JsonSerializationContext context) {
JsonObject retValue = new JsonObject();
String className = src.getClass().getCanonicalName();
retValue.addProperty(CLASSNAME, className);
JsonElement elem = context.serialize(src);
retValue.add(INSTANCE, elem);
return retValue;
}
}
然后,当我尝试序列化ArrayList时:
String json2 = gson.toJson(behaviors);
我的堆栈溢出了
似乎在网上:
JsonElement elem = context.serialize(src);
它启动一个递归循环,一次又一次地遍历我的序列化程序。那么我如何注册它,这样就不会发生这种情况呢?我需要序列化列表并维护多态性。请查看。该类的示例如下:
RuntimeTypeAdapterFactory<BillingInstrument> rta = RuntimeTypeAdapterFactory.of(
BillingInstrument.class)
.registerSubtype(CreditCard.class);
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(rta)
.create();
CreditCard original = new CreditCard("Jesse", 234);
assertEquals("{\"type\":\"CreditCard\",\"cvv\":234,\"ownerName\":\"Jesse\"}",
gson.toJson(original, BillingInstrument.class));
BillingInstrument deserialized = gson.fromJson(
"{type:'CreditCard',cvv:234,ownerName:'Jesse'}", BillingInstrument.class);
assertEquals("Jesse", deserialized.ownerName);
assertTrue(deserialized instanceof CreditCard);
RuntimeTypeAdapterFactory rta=RuntimeTypeAdapterFactory.of(
BillingInstrument.class)
.registerSubtype(CreditCard.class);
Gson Gson=new GsonBuilder()
.registerTypeAdapterFactory(rta)
.create();
信用卡原件=新信用卡(“杰西”,234);
资产质量(“{”类型\“:”信用卡\“,”cvv \“:234,\”所有者姓名\“:\”杰西\“}”,
toJson(原件,BillingInstrument.class));
BillingInstrument反序列化=gson.fromJson(
“{类型:'CreditCard',cvv:234,所有者名称:'Jesse'}”,BillingInstrument.class);
assertEquals(“Jesse”,反序列化.ownerName);
assertTrue(信用卡的反序列化实例);
该类不在核心Gson中;您需要将其复制到项目中才能使用。看起来您找到了无限循环: 但是,永远不要在src对象本身上调用它,因为这将导致无限循环(Gson将再次调用回调方法) 我能想到的最简单的方法是创建一个没有安装处理程序的新Gson实例,然后运行您的实例 作为结束运行,您可以只序列化
列表
:
public class BehaviorListAdapter implements JsonSerializer<List<Behavior>> {
private static final String CLASSNAME = "CLASSNAME";
private static final String INSTANCE = "INSTANCE";
@Override
public JsonElement serialize(List<Behavior> src, Type typeOfSrc,
JsonSerializationContext context) {
JsonArray array = new JsonArray();
for (Behavior behavior : src) {
JsonObject behaviorJson = new JsonObject();
String className = behavior.getClass().getCanonicalName();
behaviorJson.addProperty(CLASSNAME, className);
JsonElement elem = context.serialize(behavior);
behaviorJson.add(INSTANCE, elem);
array.add(behaviorJson);
}
return array;
}
}
GsonBuilder builder = new GsonBuilder();
// use a TypeToken to make a Type instance for a parameterized type
builder.registerTypeAdapter(
(new TypeToken<List<Behavior>>() {}).getType(),
new BehaviorListAdapter());
gson = builder.create();
公共类BehaviorListAdapter实现JsonSerializer{
私有静态最终字符串CLASSNAME=“CLASSNAME”;
私有静态最终字符串INSTANCE=“INSTANCE”;
@凌驾
公共JsonElement序列化(列表src,类型typeOfSrc,
JsonSerializationContext(上下文){
JsonArray数组=新的JsonArray();
for(行为:src){
JsonObject behaviorJson=新的JsonObject();
字符串className=behavior.getClass().getCanonicalName();
addProperty(CLASSNAME,CLASSNAME);
JsonElement elem=context.serialize(行为);
add(实例,elem);
add(behaviorJson);
}
返回数组;
}
}
GsonBuilder=新的GsonBuilder();
//使用TypeToken为参数化类型创建类型实例
builder.registerTypeAdapter(
(新的TypeToken(){}).getType(),
new BehaviorListAdapter());
gson=builder.create();
我明白你在这里想做什么,我也有同样的问题
我写了一个简单的抽象类
public abstract class TypedJsonizable extends Jsonizable {}
并将TypeHierarchyAdapter注册到我的Gson实例
protected static Gson gson = new GsonBuilder()
.registerTypeHierarchyAdapter
(TypedJsonizable.class,new TypedJsonizableSerializer());
此TypeAdapter的关键是不调用context.serialize和context.deserialize,因为这将导致一个无限循环,正如他的回答中所述,此TypeAdapter使用反射来避免这种情况
import com.google.gson.*;
import org.apache.log4j.Logger;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
public class TypedJsonizableSerializer implements JsonSerializer<TypedJsonizable>, JsonDeserializer<TypedJsonizable> {
static final String CLASSNAME_FIELD = "_className";
Logger logger = Logger.getLogger(TypedJsonizable.class);
@Override
public JsonElement serialize(TypedJsonizable src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject contentObj = new JsonObject();
contentObj.addProperty(CLASSNAME_FIELD,src.getClass().getCanonicalName());
for (Field field : src.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
if (field.get(src)!=null)
contentObj.add(field.getName(),context.serialize(field.get(src)));
} catch (IllegalAccessException e) {
logger.error(e.getMessage(),e);
}
}
return contentObj;
}
@Override
public TypedJsonizable deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
String className = jsonObject.get(CLASSNAME_FIELD).getAsString();
if (className == null || className.isEmpty())
throw new JsonParseException("Cannot find _className field. Probably this instance has not been serialized using Jsonizable jsonizer");
try {
Class<?> clazz = Class.forName(className);
Class<?> realClazz = (Class<?>) typeOfT;
if (!realClazz.equals(clazz))
throw new JsonParseException(String.format("Cannot serialize object of class %s to %s", clazz.getCanonicalName(),realClazz.getCanonicalName()));
Object o = clazz.getConstructor().newInstance();
for (Field field : o.getClass().getDeclaredFields()) {
field.setAccessible(true);
if (jsonObject.has(field.getName())) {
field.set(o,context.deserialize(jsonObject.get(field.getName()) , field.getGenericType()));
}
}
return (TypedJsonizable) o;
} catch (ClassNotFoundException e) {
throw new JsonParseException(String.format("Cannot find class with name %s . Maybe the class has been refactored or sender and receiver are not using the same jars",className));
} catch (IllegalAccessException e){
throw new JsonParseException(String.format("Cannot deserialize, got illegalAccessException %s ",e.getMessage()));
} catch (NoSuchMethodException | InstantiationException | InvocationTargetException e) {
throw new JsonParseException(String.format("Cannot deserialize object of class %s, unable to create a new instance invoking empty constructor",className));
}
}
import com.google.gson.*;
导入org.apache.log4j.Logger;
导入java.lang.reflect.Field;
导入java.lang.reflect.InvocationTargetException;
导入java.lang.reflect.Type;
公共类TypedJsonizableSerializer实现JsonSerializer、JsonDeserializer{
静态最终字符串CLASSNAME\u字段=“\u CLASSNAME”;
Logger Logger=Logger.getLogger(TypedJsonizable.class);
@凌驾
公共JsonElement序列化(TypedJsonizable src、typeOfSrc、JsonSerializationContext上下文){
JsonObject contentObj=新的JsonObject();
contentObj.addProperty(CLASSNAME_字段,src.getClass().getCanonicalName());
for(字段:src.getClass().getDeclaredFields()){
字段。setAccessible(true);
试一试{
if(field.get(src)!=null)
add(field.getName(),context.serialize(field.get(src));
}捕获(非法访问例外e){
logger.error(e.getMessage(),e);
}
}
返回contentObj;
}
@凌驾
公共类型djsonizable反序列化(JsonElement json,类型typeOfT,JsonDeserializationContext上下文)引发JsonParseException{
JsonObject JsonObject=json.getAsJsonObject();
字符串className=jsonObject.get(className_字段).getAsString();
if(className==null | | className.isEmpty())
抛出新的JsonParseException(“找不到_className字段。可能此实例尚未使用JSONIABLE JSONIABLE进行序列化”);
试一试{
Class clazz=Class.forName(className);
类realClazz=(类)typeOfT;
如果(!realClazz.equals(clazz))
抛出新的JsonParseException(String.format(“无法将类%s的对象序列化为%s”,clazz.getCanonicalName(),realClazz.getCanonicalName());
对象o=clazz.getConstructor().newInstance();
对于(字段:o.getClass().getDeclaredFields()){
字段。setAccessible(true);
if(jsonObject.has(field.getName())){
set(o,context.deserialize(jsonObject.get(field.getName()),field.getGenericType());
}
}
返回(类型djsonalizable)o;
}catch(classnotfounde异常){
抛出新的JsonParseException(String.format(“找不到名为%s的类。可能该类已被重构,或者发送方和接收方未使用相同的JAR”,className));
}捕获(非法访问例外e){
抛出新的JsonParseException(String.format(“无法反序列化,获取了illegalAccessException%s”,e.getMessage());
}catch(NoSuchMethodException |实例化Exception |调用TargetException e){
抛出新的JsonParseException(String.format(“无法反序列化类%s的对象,无法创建调用空构造函数的新实例”,className));
}
}
}我找到了解决此问题的另一个解决方案(变通方法):不要序列化类层次结构中的基类,而是使用子类。对于
import com.google.gson.*;
import org.apache.log4j.Logger;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
public class TypedJsonizableSerializer implements JsonSerializer<TypedJsonizable>, JsonDeserializer<TypedJsonizable> {
static final String CLASSNAME_FIELD = "_className";
Logger logger = Logger.getLogger(TypedJsonizable.class);
@Override
public JsonElement serialize(TypedJsonizable src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject contentObj = new JsonObject();
contentObj.addProperty(CLASSNAME_FIELD,src.getClass().getCanonicalName());
for (Field field : src.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
if (field.get(src)!=null)
contentObj.add(field.getName(),context.serialize(field.get(src)));
} catch (IllegalAccessException e) {
logger.error(e.getMessage(),e);
}
}
return contentObj;
}
@Override
public TypedJsonizable deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
String className = jsonObject.get(CLASSNAME_FIELD).getAsString();
if (className == null || className.isEmpty())
throw new JsonParseException("Cannot find _className field. Probably this instance has not been serialized using Jsonizable jsonizer");
try {
Class<?> clazz = Class.forName(className);
Class<?> realClazz = (Class<?>) typeOfT;
if (!realClazz.equals(clazz))
throw new JsonParseException(String.format("Cannot serialize object of class %s to %s", clazz.getCanonicalName(),realClazz.getCanonicalName()));
Object o = clazz.getConstructor().newInstance();
for (Field field : o.getClass().getDeclaredFields()) {
field.setAccessible(true);
if (jsonObject.has(field.getName())) {
field.set(o,context.deserialize(jsonObject.get(field.getName()) , field.getGenericType()));
}
}
return (TypedJsonizable) o;
} catch (ClassNotFoundException e) {
throw new JsonParseException(String.format("Cannot find class with name %s . Maybe the class has been refactored or sender and receiver are not using the same jars",className));
} catch (IllegalAccessException e){
throw new JsonParseException(String.format("Cannot deserialize, got illegalAccessException %s ",e.getMessage()));
} catch (NoSuchMethodException | InstantiationException | InvocationTargetException e) {
throw new JsonParseException(String.format("Cannot deserialize object of class %s, unable to create a new instance invoking empty constructor",className));
}
}
...
protected static Gson gson;
...
GsonBuilder gsb = new GsonBuilder();
gsb.registerTypeAdapter(SomeBase.class, new MQPolymorphicSerializer<SomeBase>());
gson = gsb.create();
public class SomeBase{
public class SomeDescendant extends SomeBase {
...
}
gson.toJson(new SomeBase());
gson.toJson(new SomeDescendant());
public class MQPolymorphicSerializer<T> implements JsonSerializer<T>, JsonDeserializer<T> {
private static final String CLASSNAME = "CLASSNAME";
private static final String INSTANCE = "INSTANCE";
@Override
public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject retValue = new JsonObject();
String className = src.getClass().getName();
retValue.addProperty(CLASSNAME, className);
JsonElement elem = context.serialize(src);
retValue.add(INSTANCE, elem);
return retValue;
}
@Override
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
JsonPrimitive prim = (JsonPrimitive) jsonObject.get(CLASSNAME);
String className = prim.getAsString();
Class<?> klass = null;
try {
klass = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new JsonParseException(e.getMessage());
}
return context.deserialize(jsonObject.get(INSTANCE), klass);
}