kryo: CompatibleFieldSerializer fails when superclass fields concrete type changes
It looks like CompatibleFieldSerializer is caching the first concrete type for a field, If you try to pass a different concrete type the serializer fails.
I wrote the following code against “com.esotericsoftware:kryo:5.0.0-RC9”
It basically writes a wrapper instance with a int value, then tries to write another wrapper instance with a string value and fails.
public class KryoSerializerDemo {
public static void main(String[] args) {
Kryo kryo = new Kryo();
kryo.setRegistrationRequired(false);
CompatibleFieldSerializer.CompatibleFieldSerializerConfig config = new CompatibleFieldSerializer.CompatibleFieldSerializerConfig();
kryo.setDefaultSerializer(new SerializerFactory.CompatibleFieldSerializerFactory(config));
Output output = new Output(4096, Integer.MAX_VALUE);
kryo.writeClassAndObject(output, new Wrapper(123));
output.reset();
kryo.writeClassAndObject(output, new Wrapper("foo"));
}
public static class Wrapper {
public Wrapper(Object value) {
this.value = value;
}
Object value;
}
}
When I run the code above, I get the following exception:
Exception in thread "main" com.esotericsoftware.kryo.KryoException: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
Serialization trace:
value (com.foo.KryoSerializerDemo$Wrapper)
at com.esotericsoftware.kryo.serializers.ReflectField.write(ReflectField.java:96)
at com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer.write(CompatibleFieldSerializer.java:107)
at com.esotericsoftware.kryo.Kryo.writeClassAndObject(Kryo.java:640)
at com.foo.KryoSerializerDemo.main(KryoSerializerDemo.java:26)
Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at com.esotericsoftware.kryo.serializers.DefaultSerializers$IntSerializer.write(DefaultSerializers.java:124)
at com.esotericsoftware.kryo.Kryo.writeObject(Kryo.java:571)
at com.esotericsoftware.kryo.serializers.ReflectField.write(ReflectField.java:86)
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 15
Commits related to this issue
- #774 Do not re-use `Compatible` and `Tagged` serializers in `ReflectField` — committed to theigl/kryo by theigl 4 years ago
- #774 Do not re-use serializer instances for `Compatible` and `Tagged` serializers in `ReflectField` — committed to theigl/kryo by theigl 4 years ago
- #774 Do not re-use serializer instances for `Compatible` and `Tagged` serializers in `ReflectField` — committed to theigl/kryo by theigl 4 years ago
- Merge remote-tracking branch 'origin/master' into #774-serializer-reuse — committed to theigl/kryo by theigl 4 years ago
- #774 Do not re-use serializer instances for `Compatible` and `Tagged` serializers in `ReflectField` — committed to theigl/kryo by theigl 4 years ago
- #774 Do not re-use serializer instances for `Compatible` and `Tagged` serializers in `ReflectField` — committed to theigl/kryo by theigl 4 years ago
- #774 Do not re-use serializer instances for `Compatible` and `Tagged` serializers in `ReflectField` — committed to theigl/kryo by theigl 4 years ago
- #774 Do not re-use serializer instances for `Compatible` and `Tagged` serializers in `ReflectField` — committed to theigl/kryo by theigl 4 years ago
- #774 Do not re-use serializer instances for `Compatible` and `Tagged` serializers in `ReflectField` — committed to theigl/kryo by theigl 4 years ago
- #774 Do not re-use serializer instances for `Compatible` and `Tagged` serializers in `ReflectField` — committed to theigl/kryo by theigl 4 years ago
- #774 Do not re-use serializer instances for `Compatible` and `Tagged` serializers in `ReflectField` — committed to theigl/kryo by theigl 4 years ago
- #774 Do not re-use serializer instances for `Compatible` and `Tagged` serializers in `ReflectField` — committed to theigl/kryo by theigl 4 years ago
- #774 Do not re-use serializer instances for `Compatible` and `Tagged` serializers in `ReflectField` — committed to theigl/kryo by theigl 4 years ago
- #774 Do not re-use serializer instances for `Compatible` and `Tagged` serializers in `ReflectField` — committed to theigl/kryo by theigl 4 years ago
- #774 Do not re-use serializers for CompatibleFieldSerializer and TaggedFieldSerializer (#788) — committed to EsotericSoftware/kryo by theigl 4 years ago
- #774 Validate read types against `field.getType()` instead of `valueClass` — committed to EsotericSoftware/kryo by theigl 4 years ago
- Merge pull request #794 from EsotericSoftware/774-incompatible-read-type #774 Validate read types against field type instead of valueClass — committed to EsotericSoftware/kryo by theigl 4 years ago
@dhofftgt: Great! I’ll do a new release in a couple of days.
@dhofftgt: Thanks for providing the test-case! My test unfortunately didn’t catch this problem because it reads the payload back immediately.
This is a different issue related to #744 and should be relatively easy to fix.
I see serialization seems to work, but I’m seeing some issues on the deserializing side:
@dhofftgt: I just released 5.0.1.
@dhofftgt: I wanted to release 5.0.1 last week, but I’m still lacking some permissions/necessary infos to perform the release. Currently waiting for feedback from @magro.
The problem is that
CompatibleFieldSerializerandTaggedFieldSerializerstore a field’s last usedvalueClass:https://github.com/EsotericSoftware/kryo/blob/74db64334d9e16de95562ccd3a33dc7050dbc939/src/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializer.java#L104
This saves 1 byte for non-final fields, but makes it impossible to re-use the same serializer for all instances of the field.
There are two options to resolve this:
FieldSerializerConfigthat toggles serializer caching and turn it off by default forCompatibleFieldSerializerandTaggedFieldSerializerCachedFieldsthat only stores the value class for primitives, wrappers and final classes.The first option is serialization compatible, but does not benefit from the performance gains from serializer caching. The second option likely breaks serialization compatibility with previous RCs and produces a slightly larger binary representation for non-final fields.
I’ll try to get input on this from @NathanSweet.