Open
Description
Kryo version 5.5.0
Java 17
When data was serialized and after that a new field was added, deserilization fails with exception: com.esotericsoftware.kryo.kryo5.io.KryoBufferUnderflowException: Buffer underflow
Stacktrace
Exception in thread "main" com.esotericsoftware.kryo.kryo5.io.KryoBufferUnderflowException: Buffer underflow.
Serialization trace:
info (akta.dcs.session.live.hash.Data)
at com.esotericsoftware.kryo.kryo5.io.Input.require(Input.java:221)
at com.esotericsoftware.kryo.kryo5.io.Input.readVarIntFlag(Input.java:482)
at com.esotericsoftware.kryo.kryo5.io.Input.readString(Input.java:777)
at com.esotericsoftware.kryo.kryo5.serializers.DefaultSerializers$StringSerializer.read(DefaultSerializers.java:174)
at com.esotericsoftware.kryo.kryo5.serializers.DefaultSerializers$StringSerializer.read(DefaultSerializers.java:164)
at com.esotericsoftware.kryo.kryo5.Kryo.readObjectOrNull(Kryo.java:826)
at com.esotericsoftware.kryo.kryo5.serializers.RecordSerializer.read(RecordSerializer.java:136)
at com.esotericsoftware.kryo.kryo5.Kryo.readObject(Kryo.java:777)
Code example:
public record Data(
String id
){}
===============================================
var kryoLocal = ThreadLocal.withInitial(() -> {
Kryo kryo = new Kryo();
kryo.setRegistrationRequired(false);
kryo.setDefaultSerializer(CompatibleFieldSerializer.class);
return kryo;
});
var data = new Data("1");
var output = new Output(1024, -1);
kryoLocal.get().writeObject(output, data);
output.flush();
var serializedData = output.toBytes(); // [-126, 49]
output.close();
After serialized, new field was added:
public record Data(
String id,
String info
){}
=================
var input = new Input(new byte[] {-126, 49});
var deserializedData = kryoLocal.get().readObject(input, Data.class);
input.close();
And eventually an exception is thrown: com.esotericsoftware.kryo.kryo5.io.KryoBufferUnderflowException: Buffer underflow.
But if convert Java record to class, everything works properly:
Code example:
public class Data {
private String id;
public Data() {
}
public Data(
String id
) {
this.id = id;
}
public String id() {
return id;
}
}
=====================================
var kryoLocal = ThreadLocal.withInitial(() -> {
Kryo kryo = new Kryo();
kryo.setRegistrationRequired(false);
kryo.setDefaultSerializer(CompatibleFieldSerializer.class);
return kryo;
});
var data = new Data("1");
var output = new Output(1024, -1);
kryoLocal.get().writeObject(output, data);
output.flush();
var serializedData = output.toBytes(); // [1, 105, -28, 3, -126, 49]
output.close();
After serialized, new field was added:
public class Data {
private String id;
private String info;
public Data() {
}
public Data(
String id,
String info
) {
this.id = id;
this.info = info;
}
public String id() {
return id;
}
public String info() {
return info;
}
}
==========================================
var input = new Input(new byte[] {1, 105, -28, 3, -126, 49});
var deserializedData = kryoLocal.get().readObject(input, Data.class);
input.close();
As we can see, that serialized record and class have different bytes.