【问题标题】:Gson, can't serialize/deserialize class typeGson,无法序列化/反序列化类类型
【发布时间】:2018-04-10 16:18:18
【问题描述】:

我有这样的课程:

public class A {
    @serializedName("type")
    Class<?> type;
...
}

但是当我尝试序列化它时,我收到一条错误消息“尝试序列化 java.lang.class:java.lang.String。忘记注册 TypeAdapter?”。所以我创建了这个适配器:

public class MyTypeAdapter extends TypeAdapter<Class> {
public Class read(JsonReader in) throws IOException {
    if (in.peek() == JsonToken.NULL) {
        in.nextNull();
        return null;
    } else {
        String className = in.nextString();
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new JsonParseException("class " + className + " not found");
        }
    }
}

public void write(JsonWriter out, Class value) throws IOException {
    if (value == null) {
        out.nullValue();
    } else {
        out.value(value.getName());
    }
}

}

并像这样注册它:

new GsonBuilder().registerTypeAdapter(Class.class, new MyTypeAdapter ()).create().
fromJson(value, listType);

但我仍然遇到同样的错误。
我做错了什么?
适配器的实现看起来没问题吗?

【问题讨论】:

  • JsonClassTypeAdapter 还是MyTypeAdapter
  • @M.Prokhorov,已修复。
  • 该错误似乎与您正在做的事情无关。 fromJson 应该产生反序列化错误,而不是序列化错误。此外,奇怪的是它显示的是 String 而不是 Class 的错误(String 具有内置的类型适配器)。你能提供一个复制问题的 MCVE 吗?
  • 请注意,在反序列化器中执行Class.forName() 是一个潜在的巨大安全漏洞:您基本上是在授予 JSON 文件权限以在您的应用程序中执行一些任意代码。

标签: java gson


【解决方案1】:

我做错了什么?

Gson 将类型信息考虑在内:您试图混合 ClassClass&lt;?&gt;,它们是不同的类型,分别代表原始类型和通配符参数化类型。 从这个角度来看,Gson 不认为 Class(在您的 registerTypeAdapter 中找到)和 Class&lt;?&gt;(在您的 DTO 中找到)等效。 对于这种情况,您必须使用 registerTypeHierarchyAdapter 注册类型层次结构适配器。

适配器的实现看起来没问题吗?

是的,但可以稍微改进一下:

final class ClassTypeAdapter
        extends TypeAdapter<Class<?>> {

    // The type adapter does not hold state, so it can be easily made singleton (+ making the constructor private)
    private static final TypeAdapter<Class<?>> instance = new ClassTypeAdapter()
            // This is a convenient method that can do trivial null-checks in write(...)/read(...) itself
            .nullSafe();

    private ClassTypeAdapter() {
    }

    static TypeAdapter<Class<?>> get() {
        return instance;
    }

    @Override
    public void write(final JsonWriter out, final Class<?> value)
            throws IOException {
        // value is never a null here
        out.value(value.getName());
    }

    @Override
    public Class<?> read(final JsonReader in)
            throws IOException {
        try {
            // This will never be a null since nullSafe() is used above
            final String className = in.nextString();
            return Class.forName(className);
        } catch ( final ClassNotFoundException ex ) {
            // No need to duplicate the message generated in ClassNotFoundException
            throw new JsonParseException(ex);
        }
    }

}

因为它是@Daniel Prydensaid,这可能是一个巨大的安全问题,因为Class.forName 可能会执行代码(静态初始化程序)。 在执行Class.forName(...) 之前,您应该根据类白名单检查className。 另请注意,Class 实例不包含类型参数化(请参阅 TypeTokens 和 ParameterizedTypes 的用途),您可能希望使用其所有类型参数化对类型进行编码(易于 toString(...) 但不是不过这很容易解析——我曾经遇到过这样的问题,并通过在 JParsec 中实现解析器来解决它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-03
    • 1970-01-01
    • 1970-01-01
    • 2013-08-26
    相关资源
    最近更新 更多