ObjectOutputStreamでシリアライズしたオブジェクトを永続化したら、serialVersionUIDが変わっちゃって破滅するやつ

題名のことやったやつおる?

ここにおる。こういう例外が投げられる。

InvalidClassException: local class incompatible: stream classdesc serialVersionUID = 1083041391519420705, local class serialVersionUID = -8102506628468650634

シリアライズした文字列(stream classdesc)中に含まれるserialVersionUIDと、今JVMの中にあるクラス(local class)のserialVersionUIDが違うわボケということの模様。

時と場合によるけど、永続化するならもっとマシなシリアライズ方式使えば良かったと思う。か、serialVersionUIDを手動で設定しておくか。私が遭遇したケースでは、永続化してた値はScalaで言う値クラスみたいなやつだったので、その値そのものを入れとけば良かったのに……、と30秒天井仰いだ。

ただまあ永続化していたものはセッションでログイン中のユーザーIDとCSRFトークンで、件数も少なかった。Redisに入れてたので、正直「FLUSHALLしてええか?」ってなった。なったけど、グッと堪えてシリアライズした文字列中に含まれるserialVersionUIDを書き換えてやった(これもどうかと思うが……)。

調べると、オブジェクト直列化ストリームプロトコルの仕様に則りシリアライズされる模様。保存されていたバイト列と比較すると当然だが一致し、クラス名の後にserialVersionUIDが書かれていることが分かった。今回のケースでは以下のようなバイト列だったので:

  • 0xaced: STREAM_MAGIC
  • 0x0005: STREAM_VERSION
  • 0x73: TC_OBJECT
  • 0x72: TC_CLASSDESC
  • 0x0046: 10進数で70だが、次のクラス名の長さ
  • クラス名(70バイト)
  • serialVersionUID(8バイト) ← ここを書き換えた

でなんとかなった。なんとかなったのか?