Gson自定义序列化

6
我希望有一个自定义的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;
}

接下来的TypeAdapterFactory将会按照我希望的方式执行:

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 或其他任何类型时都能够工作,并且如果无法分配变量,则抛出某种类型匹配异常。

你能详细说明一下你的解决方案出了什么问题吗? - Damian Walczak
@DamianWalczak - 它可以正常反序列化为“CustomMessage”,但实际上它并没有命中有效载荷的类型适配器工厂...它只是使用其内置的反序列化。 - Cheetah
3个回答

4

看了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或在其之前插入我的工厂。我已经这样做了,它很有效。


请问您能否展示一下如何编写这段代码? - ocarlsen

2
我不知道你如何使用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对象,而不适用于数组或文字

我认为泛型不会那样工作?例如,“Map<String, Integer>”。 - vach
此功能主要设计用于具有继承的普通Java对象。但可能可以扩展到与映射一起使用(它与泛型无关,而是与JSON表示相关)。 - eugen

0
这个代码框架在您的示例上可以工作,但应改进并进行不同场景的测试。
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"
      }
    }
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接