我有这样的课程:
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);
但我仍然得到同样的错误
我做错了什么?
适配器的实现看起来不错吗?
答案 0 :(得分:1)
我做错了什么?
Gson考虑了类型信息:您试图混合Class
和Class<?>
,它们分别是表示原始类型和通配符参数化类型的不同类型。
从这个角度来看,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
个实例不包含类型参数化(请参阅TypeToken
和ParameterizedType
的用途),您可能希望使用其所有类型参数化对类型进行编码(到toString(...)
但不是那么容易解析 - 我曾经遇到过这样的问题,并通过在JParsec中实现解析器来解决它。)