5

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

i は次のように登録します。

GsonBuilder builder = new GsonBuilder();        
builder.registerTypeHierarchyAdapter(Behavior.class, new BehaviorAdapter());
gson = builder.create();

次に、ArrayList をシリアル化しようとすると:

String json2 = gson.toJson(behaviors);

スタック オーバーフローが発生します。

オンラインで次のように表示されます。

JsonElement elem = context.serialize(src);

再帰ループを開始し、シリアライザーを何度も繰り返します。では、このようなことが起こらないようにするにはどうすれば登録できますか? リストをシリアル化し、ポリモーフィズムを維持する必要があります。

4

4 に答える 4

11

JsonSerializerドキュメントが警告する無限ループを見つけたようです。

ただし、srcオブジェクト自体で呼び出すことは絶対にしないでください。無限ループが発生します(Gsonはコールバックメソッドを再度呼び出します)。

私が考える最も簡単な方法は、ハンドラーがインストールされていない新しいGsonインスタンスを作成し、それを介してインスタンスを実行することです。

List<Behavior>最終的には、代わりにシリアル化することができます。

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();
于 2012-11-06T07:24:17.527 に答える
7

RuntimeTypeAdapterFactoryを見てください。そのクラスのテストには例があります:

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 にはありません。使用するには、プロジェクトにコピーする必要があります。

于 2012-11-06T07:11:15.323 に答える
1

私はあなたがここでやろうとしていることを理解しています、そして私は同じ問題を抱えていました.

単純な抽象クラスを書き終えました

public abstract class TypedJsonizable extends Jsonizable {}

TypeHierarchyAdapter を Gson インスタンスに登録する

    protected static Gson gson = new GsonBuilder()
    .registerTypeHierarchyAdapter
    (TypedJsonizable.class,new TypedJsonizableSerializer());

この TypeAdapter の鍵は、 context.serialize と context.deserialize を呼び出さないことです。Jeff Bowmanの回答で述べられているように、無限ループが発生するため、この 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));
    }
}

}

于 2017-06-16T11:19:57.153 に答える