IK.AM

@making's tech note


Spring MVC + Spring BootでJackson2のdataformatを変更する方法

🗃 {Programming/Java/org/springframework/boot}
🏷 Jackson 🏷 Java 🏷 Spring 🏷 Spring Boot 🏷 Spring MVC 
🗓 Updated at 2014-12-19T13:41:07Z  🗓 Created at 2014-12-19T13:41:07Z   🌎 English Page

ややマニアックなネタです。

JSONライブラリとして最も有名なJacksonですが、Jackson2にはdataformatという機構があり、JSONでないフォーマットも扱えるようになっています。

  • CSV
  • YAML
  • XML
  • Ini
  • Smile("binary JSON")
  • ProtoBuf
  • Avro
  • Thrift

など色々用意されています

それぞれcom.fasterxml.jackson.core.JsonFactoryを実装しています。Jacksonから使う場合はcom.fasterxml.jackson.databind.ObjectMapperObjectMapper(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を扱う例を示します。

まず、以下の依存関係を追加します。

<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での例は以下の通り。

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

$ 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

$ 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

$ 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定義をしたい場合は、この辺を参照してください。

本当はMessagePackのjackson-dataformat-msgpackを使いたかったのですが、なんか結果がおかしい(二回書き込まれている)&何回かやるとレスポンスが空になるなど、怪しい挙動をしていました・・・問題の切り分けのためにここに至ったわけです・・・


✒️️ Edit  ⏰ History  🗑 Delete