我正在尝试使用 Kryo 序列化库
序列化
LinkedHashMultimap
,但在反序列化时得到 NullPointerException
。最小的工作示例如下:
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.google.common.collect.LinkedHashMultimap;
import java.io.*;
public class SerializationTest {
private static final String ioFileName = "someIO.bin";
public static void main(String[] args0) {
// Create LinkedHashMultimap to serialize
LinkedHashMultimap<String, Object> outObj = LinkedHashMultimap.create();
outObj.put("x", 1);
outObj.put("y", "abc");
// Try to serialize and deserialize
Kryo kryo = new Kryo();
writeObj(kryo, outObj);
LinkedHashMultimap<String, Object> inObj = (LinkedHashMultimap<String, Object>) readObj(kryo);
System.out.println(inObj);
}
public static Object readObj(Kryo kryo) {
Object obj = null;
try {
Input input = new Input(new FileInputStream(ioFileName));
obj = kryo.readClassAndObject(input);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return obj;
}
public static void writeObj(Kryo kryo, Object obj) {
try {
Output output = new Output(new FileOutputStream(ioFileName));
kryo.writeClassAndObject(output, obj);
output.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
只要调用
kryo.readClassAndObject()
(第 30 行)就可以看到问题(IntelliJ 14 中调试的屏幕截图):
要么
LinkedHashMultimap
在序列化时被损坏,要么只是被错误地反序列化,导致 NullPointerException
。
调用
System.out.println(inObj)
时生成的完整堆栈跟踪:
Exception in thread "main" java.lang.NullPointerException
Disconnected from the target VM, address: '127.0.0.1:60310', transport: 'socket'
at com.google.common.collect.AbstractMapBasedMultimap$AsMap.toString(AbstractMapBasedMultimap.java:1293)
at com.google.common.collect.AbstractMultimap.toString(AbstractMultimap.java:239)
at com.google.common.collect.LinkedHashMultimap.toString(LinkedHashMultimap.java:81)
at java.lang.String.valueOf(String.java:2854)
at java.io.PrintStream.println(PrintStream.java:821)
at SerializationTest.main(SerializationTest.java:23)
有谁知道如何解决这个问题吗?
为了帮助远程调试 (Java),能够请求远程服务器将任意对象发送到我的本地计算机进行检查非常有用。然而,这意味着远程服务器必须能够序列化运行时事先未知的任意 java 对象。
所以我四处打听,偶然发现了Kryo序列化库。从来看,一个主要特点是它在序列化任意java对象方面非常健壮。物体
Serializable
,我今天遇到了类似的问题,因为我花了一些时间来确定观察到的症状背后的根本原因,所以我相信发布答案是有价值的,即使这个问题已经存在了一段时间。值得注意的是,这个问题也在 GitHub 上进行了讨论:https://github.com/EsotericSoftware/kryo/issues/573。
重要的是要了解
multimapHeaderEntry
类中的 LinkedHashMultimap
属性被标记为 transient
。根据 https://github.com/EsotericSoftware/kryo#fieldserializer-settings,Kryo 默认情况下不会序列化瞬态字段。要仍然序列化瞬态字段,您可以通过 FieldSerializerConfig 配置默认字段序列化器。请参阅下面的 MRE:
public class Debug {
static class Dummy {
int i;
transient int ti;
}
@Test
public void kryoTest() {
// kryo setup
Kryo kryo = new Kryo();
FieldSerializer.FieldSerializerConfig config = new FieldSerializer.FieldSerializerConfig();
config.setSerializeTransient(true); // test will fail if this line is commented out
kryo.addDefaultSerializer(Dummy.class, new FieldSerializer<>(kryo, Dummy.class, config));
kryo.register(Dummy.class); // registration after configuration
// create dummy object
Dummy dummy = new Dummy();
dummy.i = 1;
dummy.ti = 2;
// serialize
Output output = new Output(128, -1);
kryo.writeObject(output, dummy);
byte[] data = output.getBuffer();
// deserialize
Dummy clone = kryo.readObject(new Input(data), Dummy.class);
assertEquals(dummy.i, clone.i);
assertEquals(dummy.ti, clone.ti); // fails if you do not use the custom serializer above
}
}
在进行序列化/反序列化之前尝试下一步(在我的例子中,它适用于 kryo.WriteObject 和 kryo.readObject)
JavaSerializer serializer = new JavaSerializer();
kryo.register(LinkedHashMultimap.class, serializer);
工作示例:
public class SerializationTest {
private static final String ioFileName = "someIO.bin";
public static void main(String[] args0) {
// Create LinkedHashMultimap to serialize
LinkedHashMultimap<String, Object> outObj = LinkedHashMultimap.create();
outObj.put("x", 1);
outObj.put("y", "abc");
// Try to serialize and deserialize
Kryo kryo = new Kryo();
kryo.register(LinkedHashMultimap.class, new JavaSerializer());
writeObj(kryo, outObj);
LinkedHashMultimap<String, Object> inObj = (LinkedHashMultimap<String, Object>) readObj(kryo);
System.out.println(inObj);
}
public static Object readObj(Kryo kryo) {
Object obj = null;
try {
Input input = new Input(new FileInputStream(ioFileName));
obj = kryo.readClassAndObject(input);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return obj;
}
public static void writeObj(Kryo kryo, Object obj) {
try {
Output output = new Output(new FileOutputStream(ioFileName));
kryo.writeClassAndObject(output, obj);
output.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
输出:
{x=[1], y=[abc]}