Java で JSON を編集するときによく使われるのが Jackson ライブラリであろう。
巨大で階層が深い中の値を変更するのは、Jackson の JsonNode としてJSONを読み込んで、コードを書くのも
面倒くさいばかりでなく、あまり汎用的なものは期待できない。
PostgreSQL が使えるという前提にはなるが、PostgreSQL の JSON関数を
テーブルを指定しないクエリの実行(SELECT 文で PostgreSQLの JSON関数)をしてしまえば、
楽であろう。
DB接続するからその分パフォーマンスが落ちるものの、魅かれてしまう方法である。
(馬鹿げているかもしれないが面白い)
次の方法にする。
- mybatis のアノテーション@Select でインターフェースだけで用意
- mybatis のSQLMap XMLではクエリを書かない、@Select でJSON関数実行を書く
- SQLMapper であるインターフェースに、デフォルトメソッドで @Select のメソッドを実行するようにラップする
import java.util.Arrays; import java.util.stream.Collectors; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; /** * JsonEditMapper */ public interface JsonEditMapper{ /** * JSONテキスト内Path指定置換 * @param json JSONテキスト文字列 * @param value 置換後の値、 * @param path pathとしてキーをルートから並べた配列 * @return 置換後のJSONテキスト文字列 */ default String replace(String json, Object value, String...path) { String val; if (value==null) { val = "null"; }else if(value instanceof Boolean) { val = value.toString(); }else if(value instanceof Number) { val = value.toString(); }else{ val = "\"" + value + "\""; } String paths = Arrays.stream(path).collect(Collectors.joining(",")); return jsonbset(json, paths, val); } @Select("SELECT jsonb_set('${json}'::jsonb, '{${path}}', '${value}', false)") String jsonbset(@Param("json")String json, @Param("path")String path, @Param("value")String value); }
@Select のメソッドに public スコープを付けていない。@Select のメソッドを意識せずに
デフォルトメソッドを使うようにする。
jsonb_set 関数の最後の引数 boolean を true にすれば、キーのパスが存在しなければ、
キーのパスを新しく作成してくれる。
使用例、
String jsontxt = """ { "task": { "name": "A", "order" : 10, "limit" : "2023-12-09" } } """;
SqlSession session が生成済であるとして、
"task"->"order" の値を変更する例
String result = session.getMapper(JsonEditMapper.class).replace(jsontxt, "Test", "task", "order"); String result = session.getMapper(JsonEditMapper.class).replace(jsontxt, true, "task", "order"); String result = session.getMapper(JsonEditMapper.class).replace(jsontxt, 1024, "task", "order"); String result = session.getMapper(JsonEditMapper.class).replace(jsontxt, null, "task", "order");