Generic Java serialization/deserialization using Kryo
Asked Answered
E

2

6

I am trying to serialize and deserialize objects of a custom class (say, SomeClass, having a default no-args constructor) to a byte[] array, using Kryo 2.19 and the default serializer (FieldSerializer).

Serialization seems to work OK, but I get various exceptions in deserialization, depending on the actual implementation of SomeClass.

The code looks something like this:

SomeClass object = getObject(); // Create and populate a new object of SomeClass

Kryo kryo = new Kryo();
FieldSerializer<?> serializer = new FieldSerializer<SomeClass>(kryo, SomeClass.class);
kryo.register(SomeClass.class, serializer);

ByteArrayOutputStream stream = new ByteArrayOutputStream();
Output output = new Output(stream);

kryo.writeObject(output, object);

output.close(); // Also calls output.flush()

byte[] buffer = stream.toByteArray(); // Serialization done, get bytes

// Deserialize the serialized object.
object = kryo.readObject(new Input(new ByteArrayInputStream(buffer)), SomeClass.class);

An example of the exceptions I am getting is:

Exception in thread "main" java.lang.IncompatibleClassChangeError: Found interface org.objectweb.asm.MethodVisitor, but class was expected
    at com.esotericsoftware.reflectasm.ConstructorAccess.insertConstructor(ConstructorAccess.java:89)
    at com.esotericsoftware.reflectasm.ConstructorAccess.get(ConstructorAccess.java:70)
    at com.esotericsoftware.kryo.Kryo.newInstantiator(Kryo.java:1009)
    at com.esotericsoftware.kryo.Kryo.newInstance(Kryo.java:1059)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.create(FieldSerializer.java:228)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:217)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:629)

It seems that parameterized types are problematic to deserialize. To test this assumption , here is a parameterized implementation of SomeClass and getObject():

class SomeClass<T extends Serializable>
{
    private final T[] elements;

    private final int first;
    private final int second;

    private SomeClass()
    {
        this.elements = null;
        this.first  = 0;
        this.second = 0;
    }

    private SomeClass(T[] elements, int first, int second)
    {
        this.elements = elements;
        this.first = first;
        this.second = second;
    }
}

SomeClass<?> getObject()
{
    String[] elements = new String[] {"This is a test", "one"};

    return new SomeClass<String>(elements, 1, 2);
}

This serializes fine, but deserialization throws the following exception (observe how the first letter of the string is not reported in the exception cause):

Exception in thread "main" com.esotericsoftware.kryo.KryoException: Unable to find class: his is a test
Serialization trace:
elements (net.cetas.parserserver.data.report.SourceDataReporter$SomeClass)
    at com.esotericsoftware.kryo.util.DefaultClassResolver.readName(DefaultClassResolver.java:132)
    at com.esotericsoftware.kryo.util.DefaultClassResolver.readClass(DefaultClassResolver.java:109)
    at com.esotericsoftware.kryo.Kryo.readClass(Kryo.java:613)
    at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:724)
    at com.esotericsoftware.kryo.serializers.DefaultArraySerializers$ObjectArraySerializer.read(DefaultArraySerializers.java:338)
    at com.esotericsoftware.kryo.serializers.DefaultArraySerializers$ObjectArraySerializer.read(DefaultArraySerializers.java:293)
    at com.esotericsoftware.kryo.Kryo.readObjectOrNull(Kryo.java:702)
    at com.esotericsoftware.kryo.serializers.FieldSerializer$ObjectField.read(FieldSerializer.java:521)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:221)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:629)

If the above class is implemented without parameterization (i.e., the elements array declared as a String[]), deserialization works as expected.

Any ideas?

Engage answered 9/8, 2012 at 9:6 Comment(4)
Can you post the 'SomeClass''s Code here? I remember that in Kryo you should register all the classes that can be serialized (example, if you class uses ArrayList, it should be registered as well). Another question, does it work for intentionally simple class?Ratiocinate
It seems to have to do with generics. The class is parameterized, i.e. SomeClass<T> with a private instance variable T[]. If that variable is removed, it works. Otherwise, numerous types of exceptions are thrown, depending on the variation.Engage
This sounds like you should use SomeClass<T extends Serializable>Disingenuous
It doesn't work even like that. Please see the expanded version of the question above.Engage
N
11
Exception in thread "main" java.lang.IncompatibleClassChangeError: Found interface org.objectweb.asm.MethodVisitor, but class was expected

Please check which version of ASM are you use in your project. MethodVisitor is an interface in ASM up to 3.3. After 4.0 version this is a class.

Links:

Novelist answered 5/2, 2013 at 10:38 Comment(0)
M
0

Make sure you use the same class version for serialization and de-serialization. If you serialize using one class version and use a different class version(like for e.g after adding or removing a field) then this error is likely to occur. Not to mean it can occur only during that situation.

Morganne answered 12/9, 2014 at 18:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.