2016-10-26 47 views
1

我已经在Java中实现了我自己的序列化程序。让我们把这称为abcSerializer。我试图序列化的对象是abc,它是一个Google Protocol Buffer类。使用kryo序列化protobuf的问题

我想用kryo框架工作序列化这个对象。经过一番研究和谷歌阅读后,我决定继续使用kryo序列化器。我没有指定任何序列化器,所以我假设kryo选择一个默认序列化器。

public class abcSerializer implements AttributeSerializer <abc> { 

    public abcSerializer() { 
     kryo = new Kryo(); 
    } 

    public static Kryo getKryo() { 
     return kryo; 
    } 

    @Override 
    public abc read(byte[] buffer) { 

    abc xyz = null; 

    ByteArrayInputStream abcsStream = new ByteArrayInputStream(buffer); 
    Input abcsStreamInput = new Input(abcsStream); 
    xyz = getKryo().readObject(abcsStreamInput, abc.class); 
    return xyz; 
} 

@Override 
public void write(byte[] buffer, abc abc) { 

    ByteArrayOutputStream abcStream = new ByteArrayOutputStream(); 
    Output abcOutput = new Output(abcStream); 

    getKryo().writeObject(abcOutput, abc); 

    abcOutput.toBytes()   
} 

}

当我这样做的writeObject,一切都很好。但是,当我做readObject时,问题就来了。 Kyro抛出以下例外。

com.esotericsoftware.kryo.KryoException: Class cannot be created (missing no-arg constructor): java.util.Collections$Unmodifiabljava.lang.IllegalStateException: Encountered error in deserializer [null value returned]. Check serializer compatibility.eRandomAccessList 

上述例外几乎是自我解释。

kryo文档如下。 “”特定类型的序列化程序使用Java代码创建该类型的新实例。诸如FieldSerializer之类的序列化器是通用的,并且必须处理创建任何类的新实例。默认情况下,如果一个类有一个零参数构造函数,那么它通过ReflectASM或反射来调用,否则会引发异常。如果零参数构造函数是私有的,则尝试使用setAccessible通过反射来访问它。如果这是可以接受的,私有零​​参数构造函数是让Kryo创建类的实例而不影响公共API的好方法。“”

现在,我有两个问题。

1)google协议缓冲区生成的类确实没有arg构造函数。但是,这似乎是一个私人。这是否是上述kryo异常的问题和根源?

2)如果是这样,如何在上述问题上前进?我的意思是,我如何编写我自己的序列化程序,并仍然使用序列化google协议缓冲区对象数据?

回答

0

这是怎么样的工作?

/** 
* This lets Kryo serialize protobufs more efficiently. 
*/ 
public class ProtobufKryo<P extends GeneratedMessage> extends Serializer<P> { 
    protected final Method parser; 

    public ProtobufKryo(Class<P> theClass) { 
     try { 
      parser = theClass.getDeclaredMethod("parseFrom", InputStream.class); 
      parser.setAccessible(true); 
     } catch (NoSuchMethodException e) { 
      throw new IllegalArgumentException(theClass.toString() + " doesn't have parser"); 
     } 
    } 

    @Override 
    public void write(Kryo kryo, Output output, P generatedMessage) { 
     try { 
      generatedMessage.writeTo(output); 
     } catch (IOException e) { 
      // This isn't supposed to happen with a Kryo output. 
      throw new RuntimeException(e); 
     } 
    } 

    @Override 
    public P read(Kryo kryo, Input input, Class<P> gmClass) { 
     try { 
      return (P)parser.invoke(null, input); 
     } catch (InvocationTargetException | IllegalAccessException e) { 
      // These really shouldn't happen 
      throw new IllegalArgumentException(e); 
     } 
    } 
} 

OK ,,一些解释....

当KRYO遇到不认识的类的对象,它退到Java序列化。不是每一个有效的,有时不起作用。

(好吧,我承认,上面的不一定是真实的。这可能是KRYO配置的一部分。它是在我工作的环境真实。)

你可以告诉它使用其自己的序列化为特定的类,但有时你可以通过为特定的类创建自定义序列化器做得更好。

以上利用了Kryo中protobufs现有的序列化。基本上,它使用现有的protobuf writeTo()parseFrom()来处理Kryo中的序列化。你将注册上面的类来序列化你的每个protobuf类。 (Protobuf类扩展为GeneratedMessage。)

写出对象只是使用正常的protobuf writeTo()方法。在protobuf中读回使用类parseFrom()方法,该方法通过构造函数中的反射找到。

Kryo k = new Kryo(); 
    k.addDefaultSerializer(MyProtobuf.class, ProtobufKryo.class); 
    k.addDefaultSerializer(MyOtherProtobuf.class, ProtobufKryo.class); 

+2

虽然这个代码片断可以解决的问题,[包括说明](HTTP://meta.stackexchange

所以,你的东西,如配置串行.com/questions/114762/explain-completely-code-based-answers)确实有助于提高帖子的质量。请记住,您将来会为读者回答问题,而这些人可能不知道您的代码建议的原因。 –