Gson,不能序列化/反序列化类类型

时间:2018-04-09 17:52:38

标签: java gson

我有这样的课程:

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

但是当我尝试序列化时,我得到一个错误说&#34;尝试序列化java.lang.class:java.lang.String。忘了注册TypeAdapter?&#34;。所以我创建了这个适配器:

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

但我仍然得到同样的错误 我做错了什么?
适配器的实现看起来不错吗?

1 个答案:

答案 0 :(得分:1)

  

我做错了什么?

Gson考虑了类型信息:您试图混合ClassClass<?>,它们分别是表示原始类型和通配符参数化类型的不同类型。 从这个角度来看,Gson不会考虑Class(在您的registerTypeAdapter中找到)和Class<?>(在您的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);
        }
    }

}

由于said @Daniel Pryden,这可能是一个巨大的安全问题,因为Class.forName可能会执行代码(静态初始值设定项)。 在className执行之前,您应该针对班级白名单检查Class.forName(...)。 另请注意,Class个实例不包含类型参数化(请参阅TypeTokenParameterizedType的用途),您可能希望使用其所有类型参数化对类型进行编码(到toString(...)但不是那么容易解析 - 我曾经遇到过这样的问题,并通过在JParsec中实现解析器来解决它。)