JSON の Jackson が勝手に Stream を close してしまう問題
問題
JSON ライブラリ Jackson の ObjectMapper を使ったシリアライズ・デシリアライズで、Stream や Reader/Writer を対象にしたメソッドを使うと、そのStream, Reader/Writer が自動的に close されてしまうので、再び読み書きしようとした際に IOException になってしまう。
(Jackson 2.7 時点)
解決策
これは、デフォルトで読み書き後に close されるようになっているためで、この自動 close を無効にすれば解決する。
ObjectMapper objectMapper = new ObjectMapper(); objectMapper.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); // read 時の自動 close を無効に objectMapper.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE); // write 時の自動 close を無効に
ObjectMapper に与える JsonFactory に対して指定してもいいので、別解としてこんな書き方も。
MessagePackFactory factory = new MessagePackFactory(); factory.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); // read 時の自動 close を無効に factory.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE); // write 時の自動 close を無効に ObjectMapper objectMapper = new ObjectMapper(factory);
こんな時に問題になる
ObjectMapper#readValue や ObjectMapper#writeValue を使って、複数のオブジェクトを順番にストリームに書き出し/から読み込みする場合、例えばこんなコードで IOException が出る。
ObjectMapper objectMapper = new ObjectMapper(); for (Object msg : msgs) { objectMapper.writeValue(out_stream, msg); // ※ 2 個目の msg を write する時に IOException になる }
ObjectMapper objectMapper = new ObjectMapper(); while(true) { Map<String, Object> msg = objectMapper.readValue(in_stream, HashMap.class); // ※ 2周目に read する時に IOException になる callback(msg); }
経緯
Jackson と MessagePack と組み合わせて、ネットワーク経由で届く MessagePack エンコードなメッセージを順次処理して〜みたいなことをしようとしてぶつかった。
参考
GitHub - msgpack/msgpack-java: MessagePack serializer implementation for Java
msgpack-java/MessagePackGenerator.java at 0.8.7 · msgpack/msgpack-java
msgpack-java/MessagePackParser.java at 0.8.7 · msgpack/msgpack-java
JsonGenerator.Feature.html#AUTO_CLOSE_TARGET (Jackson-core 2.7.0 API)
JsonParser.Feature.html#AUTO_CLOSE_SOURCE (Jackson-core 2.7.0 API)