2016-10-26 49 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)確實有助於提高帖子的質量。請記住,您將來會爲讀者回答問題,而這些人可能不知道您的代碼建議的原因。 –