--- title: Spring MVC + Spring BootでJackson2のdataformatを変更する方法 tags: ["Jackson", "Java", "Spring", "Spring Boot", "Spring MVC"] categories: ["Programming", "Java", "org", "springframework", "boot"] date: 2014-12-19T13:41:07Z updated: 2014-12-19T13:41:07Z --- ややマニアックなネタです。 JSONライブラリとして最も有名なJacksonですが、Jackson2にはdataformatという機構があり、JSONでないフォーマットも扱えるようになっています。 * CSV * YAML * XML * Ini * Smile("binary JSON") * ProtoBuf * Avro * Thrift など[色々用意されています](https://github.com/FasterXML?query=dataformat)。 それぞれ`com.fasterxml.jackson.core.JsonFactory`を実装しています。Jacksonから使う場合は`com.fasterxml.jackson.databind.ObjectMapper`の`ObjectMapper(com.fasterxml.jackson.core.JsonFactory)`コンストラクタを使えばOKです。 これを使って、Spring MVC (+ Spring Boot)で好きなフォーマットを使いHTTPでやりとりする方法を紹介します。 Spring MVCのRESTの中でシリアライズ、デシリアライズを担うのは`org.springframework.http.converter.HttpMessageConverter`です。Spring Bootでは`HttpMessageConverter`をBean定義しておけば、使える`HttpMessageConverter`が勝手に追加されます。 SpringにはJackson2のJSONを扱う`MappingJackson2HttpMessageConverter`が元々用意されていますが、この親クラスである`AbstractJackson2HttpMessageConverter`を使うことで、`ObjectMapper`とその`HttpMessageConverter`が扱うMediaTypeを変更することが出来ます。 YAMLとSmileを扱う例を示します。 まず、以下の依存関係を追加します。 ``` xml com.fasterxml.jackson.dataformat jackson-dataformat-yaml ${jackson.version} com.fasterxml.jackson.dataformat jackson-dataformat-smile ${jackson.version} ``` あとはそれぞれの`JsonFactory`を使って`ObjectMapper`を用意すれば良いです。 Spring Boot 1.2での例は以下の通り。 ``` java package com.example; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.smile.SmileFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import lombok.Data; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @RestController public class App { @Bean HttpMessageConverter httpYamlJackson2MessageConverter() { return new AbstractJackson2HttpMessageConverter( new ObjectMapper(new YAMLFactory()) /* change here */, new MediaType("application", "x-yaml")) { }; } @Bean HttpMessageConverter httpSmileJackson2MessageConverter() { return new AbstractJackson2HttpMessageConverter( new ObjectMapper(new SmileFactory()) /* change here */, new MediaType("application", "x-smile")) { }; } @Data static class Result { private final int left; private final int right; private final long answer; } @RequestMapping("calc") Result calc(@RequestParam int left, @RequestParam int right) { return new Result(left, right, left + right); } public static void main(String[] args) { SpringApplication.run(App.class, args); } } ``` 1つのリクエストに対して複数の`HttpMessageConverter`がある場合は`Accept`ヘッダ(または拡張子)でフォーマットを切り替えることが出来ます。いわゆる"Content negotiation"です。 まずはもともと設定されているJSON ``` bash $ curl -v -H "Accept: application/json" "http://localhost:8080/calc?left=10&right=100" > GET /calc?left=10&right=100 HTTP/1.1 > User-Agent: curl/7.30.0 > Host: localhost:8080 > Accept: application/json > < HTTP/1.1 200 OK < Server: Apache-Coyote/1.1 < Content-Type: application/json;charset=UTF-8 < Transfer-Encoding: chunked < Date: Fri, 19 Dec 2014 14:00:11 GMT < {"left":10,"right":100,"answer":110} ``` 次にYAML ``` bash $ curl -v -H "Accept: application/x-yaml" "http://localhost:8080/calc?left=10&right=100" > GET /calc?left=10&right=100 HTTP/1.1 > User-Agent: curl/7.30.0 > Host: localhost:8080 > Accept: application/x-yaml > < HTTP/1.1 200 OK < Server: Apache-Coyote/1.1 < Content-Type: application/x-yaml;charset=UTF-8 < Transfer-Encoding: chunked < Date: Fri, 19 Dec 2014 14:01:05 GMT < --- left: 10 right: 100 answer: 110 ``` 最後にSmile ``` bash $ curl -v -H "Accept: application/x-smile" "http://localhost:8080/calc?left=10&right=100" > User-Agent: curl/7.30.0 > Host: localhost:8080 > Accept: application/x-smile > < HTTP/1.1 200 OK < Server: Apache-Coyote/1.1 < Content-Type: application/x-smile;charset=UTF-8 < Transfer-Encoding: chunked < Date: Fri, 19 Dec 2014 14:03:25 GMT < �leftԄright$��answer$�� ``` --- jackson-dataformatを使って簡単にHTTPでやりとりするフォーマットを追加することが出来ました。 XMLのBean定義をしたい場合は、[この辺](http://terasolunaorg.github.io/guideline/1.0.1.RELEASE/ja/ArchitectureInDetail/REST.html#restful-web-servicespring-mvc)を参照してください。 本当はMessagePackの[jackson-dataformat-msgpack](https://github.com/msgpack/msgpack-java/tree/v07-develop/msgpack-jackson)を使いたかったのですが、なんか結果がおかしい(二回書き込まれている)&何回かやるとレスポンスが空になるなど、怪しい挙動をしていました・・・問題の切り分けのためにここに至ったわけです・・・