ややマニアックなネタです。
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.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を扱う例を示します。
まず、以下の依存関係を追加します。
<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を使いたかったのですが、なんか結果がおかしい(二回書き込まれている)&何回かやるとレスポンスが空になるなど、怪しい挙動をしていました・・・問題の切り分けのためにここに至ったわけです・・・