TL;DR
以下のサンプルコードの通りです。
findValueSerializer
を使った場合、最悪シリアライズ時にエラーが出るため、defaultSerializeValue
を使うべきです。
@Override
public void serialize(
Foo value,
JsonGenerator gen,
SerializerProvider provider
) throws IOException {
// 任意の変換処理
Bar converted = convert(value);
// 以下のように書くのが望ましい
provider.defaultSerializeValue(converted, gen);
// 以下のように書くのはNG
// provider.findValueSerializer(converted.getClass()).serialize(converted, gen, provider);
}
状況想定
以下のような状況を想定しています。
- 入力を任意の型に変換してシリアライズしたい
-
ObjectMapper
に登録されているシリアライザーは使いまわしたい- 例:
Jackson
からデフォルトで提供されているものや自作した共通シリアライザー
- 例:
単純に値を書き込むだけで済む場合は、gen.writeString
やgen.writeNumber
など、よりシンプルな方法を使うこともできます。
defaultSerializeValue
を使うべき理由
単純にシンプルという以外にも、findValueSerializer
を使った場合にはエラーを引き起こすリスクが有るためです。
コンテキストが正しく伝播する
findValueSerializer
を使った場合、表面上は正しく動いているように見えても、実際にはJackson
のシリアライズに関するコンテキスト伝播の仕組みが上手く働いていません。
これによって、複雑なシリアライズ処理が期待通り動かなかったり、最悪エラーになったりします。
自分はこの方法でMap
を書き込んだ所、KeySerializer
が見つからなくなってエラーとなるケースに遭遇したことがあります。
null
の考慮が不要
defaultSerializeValue
にはnull
の考慮が有るため、サンプルコードでいうconvert
がnull
を返した場合を気にする必要はありません。
一方、findValueSerializer
を使う場合、null
に対してはfindNullValueSerializer
を使うべきです。
つまり、正確を期するなら以下のように書くべきで、より複雑になります。
// 任意の変換処理
Bar converted = convert(value);
// 正確に書くと、より複雑になって嬉しくない
JsonSerializer<*> ser = converted != null
? provider.findValueSerializer(converted.getClass())
: provider.findNullValueSerializer(null);
provider.findValueSerializer(converted.getClass()).serialize(converted, gen, provider);
参考
以下で教えて頂きました