---
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)を使いたかったのですが、なんか結果がおかしい(二回書き込まれている)&何回かやるとレスポンスが空になるなど、怪しい挙動をしていました・・・問題の切り分けのためにここに至ったわけです・・・