我有一个这样的课:

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

但是我仍然遇到同样的错误。
我究竟做错了什么?
适配器的实现看起来可以吗?

最佳答案

我究竟做错了什么?

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中实现解析器解决了)。

10-07 19:21
查看更多