Java Gson多态序列化

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";

使用Gson 2.2.2,我试图序列化POJO(行为)的数组列表

我有一个适配器,它几乎是我在网上看到的内容的副本:

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);
}