こんにちは。 エキサイト株式会社の三浦です。
JavaにはJacksonというライブラリがあり、Javaコード上のデータをJSONに変換したり(serialize)、逆にJSONをJavaコード上のデータに変換したり(deserialize)してくれます。 今回は、このJacksonを使った時にある条件下で詰まった話をしていきます。
Jackson
Jacksonは、Githubのページで以下のように定義されています。
Jackson has been known as "the Java JSON library" or "the best JSON parser for Java". Or simply as "JSON for Java".
端的に言えばJava用のJSONライブラリで、JSONとJavaコード上データの相互変換等をしてくれます。 例えば、APIでリクエスト元にデータを返す時にJavaコード上のデータをJSONに変換してから返したり、キャッシュ保存時にデータをJSON化してから保存し、逆にキャッシュからデータを取得する時にJSONをJavaコード上のデータに変換する、などで使われたりします。
大体のデータであればJacksonを使えば相互変換ができるのですが、実はまだ対応していない条件があります。
それが List.of
です。
List.of と Jackson
List.of
はJava9から追加されたメソッドで、immutableなリストを提供してくれます。
すなわち、 List.of
で定義したリストは追加や削除、更新することができず、安全に取り扱えるため、非常に使い勝手がよいわけです。
List<String> sampleList = List.of("a", "b"); // 追加できない sampleList.add("c");
ただ、残念ながらこの List.of
はJacksonには対応していません。
// SAMPLE_KEYを使ってキャッシュする @Cacheable(cacheNames = CacheKeyType.SAMPLE_KEY) public List<String> getSampleList() { return List.of("a", "b"); }
このようにキャッシュをすると、キャッシュからデータを取得する時に Could not read JSON
が発生します。
キャッシュデータを見ると以下のように保存されています。
["a", "b"]
一見問題ないように見えますが、実はJacksonではJSON化する際に変換元データの型も保存するようになっており、それをJSONから戻す際に使用するという仕様になっています。
List.of
では型を保存してくれないので、Jacksonでは戻すことができないのです。
また、 List.of
の使い方によっては型を保存してくれる場合もあるのですが、どうやら List.of
で作られた型はまだJacksonで対応されていない型のようで、いずれにしろエラーが起きてしまいます。
解決方法
もっとも安易な解決方法は、 List.of
を使わないことでしょう。
例えば上記であれば、 List.of
の代わりに Arrays.asList
を使うことができます。
// SAMPLE_KEYを使ってキャッシュする @Cacheable(cacheNames = CacheKeyType.SAMPLE_KEY) public List<String> getSampleList() { return Arrays.asList("a", "b"); }
こちらだとキャッシュでは、以下のように保存されます。
[ "java.util.Arrays$ArrayList", ["a", "b"] ]
こちらであればJacksonに対応している型が保存されるため、Jacksonで問題なく戻すことができます。
可能であれば、
// 空リストを作るとき Collections.emptyList(); // 1件だけのリストを作るとき Collections.singletonList("a"); // 2件以上のリストを作るとき Arrays.asList("a", "b");
とできると、空・1件のみのリストはimmutableになるのでおすすめです。
最後に
List.of
は非常に便利ですが、このように落とし穴があるため気をつける必要があります。
状況に合わせて使い分けていきましょう。
なお、2021年9月6日現在では List.of
がJacksonで使用できませんが、今後のアップデートで使用できるようになる可能性があります。
また、見つけられていないだけで、実は現状でも使えるようになる設定がある可能性もあります。
あらかじめご了承下さい。