簡體   English   中英

Gson自定義序列化

[英]Gson custom serialization

我希望有一個自定義的GSON反序列化器,這樣每當它反序列化一個JSON對象(即大括號{ ... }內的任何東西)時,它將尋找一個$type節點,並使用其內置的反序列化功能對該類型進行反序列化。 如果沒有找到$type對象,它就會像往常那樣做。

例如,我希望這可以工作:

{
    "$type": "my.package.CustomMessage"
    "payload" : {
        "$type": "my.package.PayloadMessage",
        "key": "value"
    }
}

public class CustomMessage {

    public Object payload;
}

public class PayloadMessage implements Payload {

    public String key;
}

調用: Object customMessage = gson.fromJson(jsonString, Object.class)

所以目前我將payload類型更改為Payload接口:

public class CustomMessage {

    public Payload payload;
}

然后,以下TypeAdapaterFactory將執行我想要的操作:

final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
final PojoTypeAdapter thisAdapter = this;

public T read(JsonReader reader) throws IOException {

    JsonElement jsonElement = (JsonElement)elementAdapter.read(reader);

    if (!jsonElement.isJsonObject()) {
        return delegate.fromJsonTree(jsonElement);
    }

    JsonObject jsonObject = jsonElement.getAsJsonObject();
    JsonElement typeElement = jsonObject.get("$type");

    if (typeElement == null) {
        return delegate.fromJsonTree(jsonElement);
    }

    try {
        return (T) gson.getDelegateAdapter(
                thisAdapter,
                TypeToken.get(Class.forName(typeElement.getAsString()))).fromJsonTree(jsonElement);
    } catch (ClassNotFoundException ex) {
        throw new IOException(ex.getMessage());
    }
}

但是,我希望它在payload類型為Object或任何類型的情況下工作,如果無法分配變量,則拋出某種類型匹配異常。

我不知道如何使用Gson實現它,但默認情況下你在Genson中有這樣的功能。

要啟用它,只需:

Genson genson = new Genson.Builder().setWithClassMetadata(true).create();

您還可以為類名注冊別名:

Genson genson = new Genson.Builder().addAlias("myClass", my.package.SomeClass.class).create();

但是這有一些限制:

  • 目前你無法改變用於識別類型的密鑰,它是@class
  • 它必須存在於你的json之前的其他屬性 - 但看起來很好,因為在你的例子中就是這種情況
  • 僅適用於json對象,而不適用於數組或litterals

看看Gson的來源,我發現了我認為的問題:

// built-in type adapters that cannot be overridden
factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);
factories.add(ObjectTypeAdapter.FACTORY);

// user's type adapters
factories.addAll(typeAdapterFactories);

如您所見, ObjectTypeAdapter優先於我的工廠。

就我所見,唯一的解決方案是使用反射從列表中刪除ObjectTypeAdapter或在其之前插入我的工廠。 我做到了這一點並且有效。

此代碼框架適用於您的示例,但應使用不同的方案進行改進和測試。

public class PojoTypeAdapaterFactory implements TypeAdapterFactory {

    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
        // check types we support
        if (type.getRawType().isAssignableFrom(CustomMessage.class) || type.getRawType().isAssignableFrom(PayloadMessage.class)) {
            return new PojoTypeAdapter<T>(gson, type);
        }
        else return null;
    }

    private class PojoTypeAdapter<T> extends TypeAdapter<T> {

        private Gson gson;

        private TypeToken<T> type;

        private PojoTypeAdapter(final Gson gson, final TypeToken<T> type) {
            this.gson = gson;
            this.type = type;
        }

        public T read(JsonReader reader) throws IOException {
            final TypeAdapter<T> delegate = gson.getDelegateAdapter(PojoTypeAdapaterFactory.this, this.type);
            final TypeAdapter<JsonElement> elementAdapter = this.gson.getAdapter(JsonElement.class);
            JsonElement jsonElement = elementAdapter.read(reader);

            if (!jsonElement.isJsonObject()) {
                return (T) this.gson.getAdapter(JsonElement.class).fromJsonTree(jsonElement);
            }

            JsonObject jsonObject = jsonElement.getAsJsonObject();
            JsonElement typeElement = jsonObject.get("$type");

            if (typeElement == null) {
                return delegate.fromJsonTree(jsonElement);
            }

            try {
                final Class myClass = Class.forName(typeElement.getAsString());
                final Object myInstance = myClass.newInstance();
                final JsonObject jsonValue = jsonElement.getAsJsonObject().get("value").getAsJsonObject();
                for (Map.Entry<String, JsonElement> jsonEntry : jsonValue.entrySet()) {
                    final Field myField = myClass.getDeclaredField(jsonEntry.getKey());
                    myField.setAccessible(true);
                    Object value = null;
                    if (jsonEntry.getValue().isJsonArray()) {
                        //value = ...;
                    }
                    else if (jsonEntry.getValue().isJsonPrimitive()) {
                        final TypeAdapter fieldAdapter = this.gson.getAdapter(myField.getType());
                        value = fieldAdapter.fromJsonTree(jsonEntry.getValue());
                    }
                    else if (jsonEntry.getValue().isJsonObject()) {
                        value = this.fromJsonTree(jsonEntry.getValue());
                    }
                    myField.set(myInstance, value);
                }
                return (T) myInstance;

            }
            catch (ClassNotFoundException | IllegalAccessException | IllegalArgumentException | InstantiationException | NoSuchFieldException | SecurityException e) {
                throw new IOException(e);
            }
        }

        @Override
        public void write(final JsonWriter out, final T value) throws IOException {
            out.beginObject();
            out.name("$type");
            out.value(value.getClass().getName());
            out.name("value");
            final TypeAdapter<T> delegateAdapter = (TypeAdapter<T>) this.gson.getDelegateAdapter(PojoTypeAdapaterFactory.this, TypeToken.<T>get(value.getClass()));
            delegateAdapter.write(out, value);
            out.endObject();
        }
    }

}

生成的JSON雖然不完全相同,因為它包含一個額外的value條目:

{
  "$type": "my.package.CustomMessage",
  "value": {
    "payload": {
      "$type": "my.package.PayloadMessage",
      "value": {
        "key": "hello"
      }
    }
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM