メモ。
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; }