メモ。
SpringのControllerでは、 @RequestBody を使用することでHTTPリクエストボディに設定されたパラメータをJSONで受け取ることができます。
@RestController public class FluitController { // Request Body にJSONを指定するAPI @RequestMapping(value = "/fluits", method = RequestMethod.POST) public FluitResponse post(@RequestBody FluitRequest req) { .... } }
このとき、 HogeRequest が以下のような構造で定義されているとします。
@Data public class FluitRequest { private String name; private Integer price; private Integer category; }
FluitRequest::category は以下の値域のみとり得るとします
HogeRequest req = new HogeRequest(); req.setCategory(1); // 1 : りんご req.setCategory(2); // 2 : みかん req.setCategory(3); // 3 : ぶどう
FluitRequest::category のような有限集合の場合、Javaであれば列挙型を使用して定義したいところです。
public enum FluitCategory { APPLE, ORANGE, GRAPE }
FluitRequest にAPI /fluits のPOSTリクエストボディがマッピングされるとき、category プロパティの値がそのまま FluitCategory 列挙型としてマッピングされたら便利ですね。
Jacksonが提供する JsonDeserializer を拡張定義することで、独自のマッピングを実装することができます。これを利用し、リクエストボディマッピング用クラス FluitRequest に型としてFluitCategory を持つプロパティを定義できるようにします。
まず、列挙型 FluitCategory を以下のように整数型IDとの紐付けます。
// 各列挙型オブジェクトがidを持つ @Getter public enum FluitCategory { APPLE(1), ORANGE(2), GRAPE(3); private int id; private FluitCategory(int id) { this.id = id; } public static FluitCategory value(int id) { // idとマッチするFluitCategoryオブジェクトがない場合独自例外を送出 return Arrays.stream(values()).filter(x -> x.id == id).findFirst().orElseThrow(() -> new HogeException()); } }
次に、以下のような JsonDeserializer 拡張クラスを定義します。
拡張する JsonDeserializer のジェネリクス型には変換先の型である FluitCategory を指定しています。
変換元の値は deserialize メソッドの引数 jsonParser から取得できます。
今回は元々整数型の値であった category を変換したかったので、 getIntValue で値を取得します。この値を元に FluitCategory::value() でFluitCategory に変換し返り値とします。
public class FluitCategoryDeserializer extends JsonDeserializer<FluitCategory> { @Override public FluitCategory deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException { return FluitCategory.value(jsonParser.getIntValue()); } }
最後に、@Bean として以下のような ObjectMapper を定義します。
ここで定義されたObjectMapperがRequest BodyのJSONパース時に使用され、クライアントから整数型で送られたプロパティを FluitCategory 型プロパティとしてJava側で受け取った際に変換が実行されるようになります。
@Configuration public class JsonConfiguration { @Bean public ObjectMapper jsonObjectMapper() { ObjectMapper mapper = new ObjectMapper(); SimpleModule simpleModule = new SimpleModule(); simpleModule.addDeserializer(FluitCategory.class, new FluitCategoryDeserializer()); mapper.registerModule(simpleModule()); return mapper; }