首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Gson多态序列化

Gson多态序列化
EN

Stack Overflow用户
提问于 2012-11-06 13:11:35
回答 4查看 7K关注 0票数 4

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

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

代码语言:javascript
复制
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;
    }
}

我像这样注册它:

代码语言:javascript
复制
GsonBuilder builder = new GsonBuilder();        
builder.registerTypeHierarchyAdapter(Behavior.class, new BehaviorAdapter());
gson = builder.create();

然后当我尝试序列化我的ArrayList时:

代码语言:javascript
复制
String json2 = gson.toJson(behaviors);

我得到一个堆栈溢出。

看起来是这样的:

代码语言:javascript
复制
JsonElement elem = context.serialize(src);

它开始一个递归循环,一次又一次地通过我的序列化程序。那么我如何注册它才不会发生这种情况呢?我需要序列化列表并维护多态。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-11-06 15:24:18

看起来你找到了无限循环the JsonSerializer docs warn about

然而,你永远不应该在src对象本身调用它,因为那会导致无限循环(Gson会再次调用你的回调方法)。

我能想到的最简单的方法是创建一个没有安装处理程序的新Gson实例,然后运行您的实例。

作为结束运行,您可以只序列化List<Behavior>

代码语言:javascript
复制
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();
票数 11
EN

Stack Overflow用户

发布于 2012-11-06 15:11:15

看看RuntimeTypeAdapterFactory吧。该类的test有一个示例:

代码语言:javascript
复制
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);

这个类不在核心Gson中;您需要将它复制到您的项目中才能使用它。

票数 7
EN

Stack Overflow用户

发布于 2017-06-16 19:19:57

我知道你想在这里做什么,我也有同样的问题。

我写完了一个简单的抽象类。

代码语言:javascript
复制
public abstract class TypedJsonizable extends Jsonizable {}

并将TypeHierarchyAdapter注册到我的Gson实例

代码语言:javascript
复制
    protected static Gson gson = new GsonBuilder()
    .registerTypeHierarchyAdapter
    (TypedJsonizable.class,new TypedJsonizableSerializer());

这个TypeAdapter的关键不是调用context.serialize和context.deserialize,因为这会导致一个无限循环,正如Jeff Bowman在他的回答中所说的,这个TypeAdapter使用反射来避免这种情况。

代码语言:javascript
复制
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));
    }
}

}

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13244769

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档