Gson自定义序列化

时间:2014-05-16 13:01:25

标签: java json serialization gson

我希望有一个自定义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或任何类型,并且如果无法分配变量,则抛出某种类型匹配异常。< / p>

3 个答案:

答案 0 :(得分: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对象,而不适用于数组或litterals

答案 1 :(得分:2)

看看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或在我之前插入我的工厂。我做到了这一点并且有效。

答案 2 :(得分: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"
      }
    }
  }
}