---
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
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-yaml</artifactId>
    <version>${jackson.version}</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-smile</artifactId>
    <version>${jackson.version}</version>
</dependency>
```

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