我开始意识到,在序列化应用程序中的对象时,我需要将类名作为一个属性包括进来。如果我为任何序列化的非基元对象添加类名属性,可能会是最好的。
我看到这是Genson的useClassMetadata
方法的一个内置特性,但是我已经在我的项目中使用了gson,所以如果我能坚持使用它,那将是非常有益的。
这是我目前的尝试:
package com.mycompany.javatest;
import com.google.gson.*;
import java.lang.reflect.*;
public class JavaTest {
public static class GenericSerializer implements JsonSerializer<Object>, JsonDeserializer<Object> {
private static final String CLASS_PROPERTY_NAME = "class";
@Override
public JsonElement serialize(Object src, Type typeOfSrc,
JsonSerializationContext context) {
JsonElement retValue = context.serialize(src);
if (retValue.isJsonObject()) {
retValue.getAsJsonObject().addProperty(CLASS_PROPERTY_NAME, src.getClass().getName());
}
return retValue;
}
@Override
public Object deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
Class actualClass;
if (json.isJsonObject()) {
JsonObject jsonObject = json.getAsJsonObject();
String className = jsonObject.get(CLASS_PROPERTY_NAME).getAsString();
try {
actualClass = Class.forName(className);
}
catch (ClassNotFoundException e) {
e.printStackTrace();
throw new JsonParseException(e.getMessage());
}
}
else {
actualClass = typeOfT.getClass();
}
return context.deserialize(json, actualClass);
}
}
public static class MyClass {
private final String name = "SpongePants SquareBob";
}
public static void main(String[] args) {
MyClass obj = new MyClass();
GsonBuilder gb = new GsonBuilder();
gb.registerTypeAdapter(Object.class, new GenericSerializer());
Gson gson = gb.create();
System.out.println(gson.toJson(obj, Object.class));
}
}
打印
{"name":"SpongePants SquareBob"}
我想打印
{"name":"SpongePants SquareBob","class":"com.mycompany.javatest$MyClass"}
**编辑:**另一次尝试(这次使用GsonFire)
package com.mycompany.javatest;
import com.google.gson.*;
import io.gsonfire.*;
public class JavaTest {
public static class DummyData {
private final String someData = "1337";
}
private static final String CLASS_PROPERTY_NAME = "class";
public static void main(String[] args) {
GsonFireBuilder gfb = new GsonFireBuilder();
gfb.registerPostProcessor(Object.class, new PostProcessor<Object>() {
@Override
public void postDeserialize(Object t, JsonElement je, Gson gson) {
// Ignore
}
@Override
public void postSerialize(JsonElement je, Object t, Gson gson) {
if (je.isJsonObject()) {
je.getAsJsonObject().add(CLASS_PROPERTY_NAME, new JsonPrimitive(t.getClass().getTypeName()));
}
}
});
gfb.registerTypeSelector(Object.class, (JsonElement je) -> {
System.out.println(je);
if (je.isJsonObject()) {
try {
return Class.forName(je.getAsJsonObject().get(CLASS_PROPERTY_NAME).getAsString());
}
catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
}
return null;
});
Gson gson = gfb.createGson();
DummyData dd = new DummyData();
String json = gson.toJson(dd);
System.out.println(json);
DummyData dd2 = (DummyData) gson.fromJson(json, Object.class); // <-- gives me a ClassCastException
}
}
4条答案
按热度按时间mefy6pfw1#
又是一个答案,花的时间有点长。
旁注:如果你递归地使用反射来计算类中的字段,上面的解决方案将有效。然后用特殊的序列化器序列化这些字段,同时为父对象使用单独的序列化器。这将避免堆栈溢出。
话虽如此-我是一个懒惰的开发人员,所以我喜欢做懒惰的事情。我正在为你改编一个谷歌解决方案。
注意:请测试并根据您的需要进行调整。这是一个原型,我尚未清理不必要的代码或检查可能的问题〉
代码的原始来源:
https://github.com/google/gson/blob/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java
所以,这是基于
RuntimeTypeAdapterFactory
的。这个工厂是由google提供的,它的目的是支持分层反序列化。要做到这一点,你需要注册一个基类和所有子类,并添加一个属性作为标识符。如果你读了javadoc,这将变得更清楚。这显然提供了我们想要的东西:递归地为类类型注册不同的适配器,这些适配器可以处理这些问题,而不会循环运行并导致堆栈溢出。你必须注册ALL子类。这显然是不合适的(尽管有人可能会说你可以在启动时解析类路径并简单地添加所有的类,这样就可以在任何地方使用它)。所以我查看了源代码并修改了代码来动态地完成这一操作。注意,google警告不要这样做--根据自己的情况使用它:)
这是我的工厂:
我已经为你添加了所有的导入。这并不是(真的)在maven central上发布的,尽管你可以在这里找到它:https://mvnrepository.com/artifact/org.danilopianini/gson-extras/0.1.0
无论如何,你必须进行修改才能让它为你工作,所以我做了一个副本。副本完全编译,你可以简单地将它粘贴到你的代码中,并为自己节省额外的依赖。
此代码的重要位如下:(我故意把它们留在里面,但注解掉了,这样你就能看出来了)
以
create(Gson gson, TypeToken<R> type)
为单位检查原始类型是否可以从String类中赋值。您希望将其应用于每个类对象,因此这会解决这个问题。注意前面的代码将查找类型是否已注册到类中-不再需要(因此不需要变量;您应该清理代码)
在
@Override public void write(JsonWriter out, R value) throws IOException {
中:首先,我们去掉标签。我们的标签是并且将永远是源类型的名称。这是在以下代码中完成的:
String label = srcType.getName();
第二,我们必须区分基本类型和对象类型。基本类型是字符串,整数等在Gson世界。这意味着我们上面的检查(添加适配器)没有捕捉到这些对象类型实际上是基本类型的事实。所以我们做:
如果它是原始的,就把树写入流中。如果它不是,我们就把所有其他的字段AND类字段写入流中。
最后,这个例子可以证明我的代码做了(我相信)你希望它做的事情;)
现在它会为您输出这个json:
正如你所看到的,字符串,长,整数都被正确地创建了。每个类对象都递归地得到了它的类名。
这是一个通用的方法,应该可以和你创建的任何东西一起工作。)就像我之前提到的,我对这个实现进行了原型化。
希望这能让我得到一个滴答声:)
此致!
阿图尔
fiei3ece2#
接受了@pandaadb的答案,但只是想粘贴我正在使用的代码。它负责用type序列化和反序列化到正确的subtybe中:
798qvoo83#
我自己也试过了,这似乎很管用:
这将打印:
详细信息:
您必须注册一个Hierarchy适配器,这样,如果您向Object类注册它,则将为您传递给它的任何类型调用它。
您还必须在自定义序列化程序中使用不同的Gson示例,否则您只能不断地循环运行,并得到Stackoverflow。
除此之外,相当直接:)
注意:我对gson的经验相当少,所以可能有一个更酷的解决方案。
此致!
阿图尔
42fyovps4#
pandaadb的惊人答案并不完全适用于我,因为它不处理数组/列表,而且反序列化也有问题,所以我做了一些修改:
不过,所有的功劳还是要归功于他/她。正如他/她所说,请在使用之前测试一下这段代码!