jacksonについて調べてみたときのメモです。
jacksonのリポジトリはこちら。
https://github.com/FasterXML/jackson
jacksonとは、JavaのJSONライブラリ、というところまでは前提知識があります。
以前からそれとなく使っていて、Spring frameworkを使っていればフレームワークがデフォルトで使用しているはずです。
jacksonは3つの要素から構成されているようです。
- https://github.com/FasterXML/jackson-core
- https://github.com/FasterXML/jackson-databind
- https://github.com/FasterXML/jackson-annotations
他にもコミュニティによるモジュールがあるようですが、上記のモジュールはjackson開発チームで開発、メンテナンスがされているそう。
サードパーティのモジュール一覧はこちらに記載がありました。思っていたより多いです。
https://github.com/FasterXML/jackson?tab=readme-ov-file#third-party-datatype-modules
databindリポジトリにチュートリアルがありました。やってみましょう。
https://github.com/FasterXML/jackson-databind
環境
java -version openjdk version "17.0.8.1" 2023-08-24 OpenJDK Runtime Environment Temurin-17.0.8.1+1 (build 17.0.8.1+1) OpenJDK 64-Bit Server VM Temurin-17.0.8.1+1 (build 17.0.8.1+1, mixed mode, sharing)
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>jackson-my-playground</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <jackson.version>2.16.0</jackson.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.junit</groupId> <artifactId>junit-bom</artifactId> <version>5.10.1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
read and write
package org.example; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class ReadWriteTest { @Test void readValue() throws JsonProcessingException { var objectMapper = new ObjectMapper(); var person = objectMapper.readValue(""" { "firstName": "book", "lastName": "store" } """, Person.class); assertEquals("firstName: book, lastName: store", person.toString()); } @Test void writeValue() throws JsonProcessingException { var objectMapper = new ObjectMapper(); var output = objectMapper.writeValueAsString(new Person("book", "store")); assertEquals("{\"firstName\":\"book\",\"lastName\":\"store\"}", output); } public static class Person { @SuppressWarnings("unused") // jackson use public Person() { } public Person(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String firstName; public String lastName; @Override public String toString() { return String.format("firstName: %s, lastName: %s", firstName, lastName); } } }
jacksonによるJSONの読み込み、書き込みはObjectMapper
を使います。ObjectMapper
クラスはインスタンス化することで使用できます。
JSONをPOJOで読み込むにはreadValue
を使います。反対に書き込むにはwriteValue
を使います。
書き込んだ結果をString
で貰いたいのであれば、writeValueAsString
を使います。
読み込む先のPOJOはデフォルトコンストラクタ(引数なしコンストラクタ)が必要になりました。おそらくですが、読み込み元のJSONをPOJOにどうやって変換するかどうかは変更できると思うので、上記のコードのようにデフォルトでは必要ということでしょう。
collection
JSONの読み込み、書き込みはPOJOだけではありません。以下のようにCollectionのMapやListを指定することも可能です。
package org.example; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.exc.MismatchedInputException; import org.junit.jupiter.api.Test; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; public class GenericCollectionTest { final ObjectMapper objectMapper = new ObjectMapper(); @Test void readAsMap() throws JsonProcessingException { @SuppressWarnings("unchecked") Map<String, String> result = objectMapper.readValue(""" { "firstName": "book", "lastName": "store" } """, Map.class); assertEquals(Map.of("firstName", "book", "lastName", "store"), result); } @Test void readAsList() throws JsonProcessingException { @SuppressWarnings("unchecked") List<Integer> result = objectMapper.readValue(""" [1, 2, 3] """, List.class); assertEquals(List.of(1, 2, 3), result); } @Test void cantReadObjectToList() { assertThrows(MismatchedInputException.class, () -> objectMapper.readValue(""" { "firstName": "book", "lastName": "store" } """, List.class)); } @Test void readAsMapPersonRecord() throws JsonProcessingException { var people = objectMapper.readValue(""" { "NB001": { "firstName": "book", "lastName": "store" } } """, new TypeReference<Map<Id, Person>>() { }); assertEquals(Map.of(new Id("NB001"), new Person("book", "store")), people); } public record Id(String value) { } public record Person(String firstName, String lastName) { } }
readAsMapPersonRecord
ではMapで読み込みをしつつ、valueにRecordクラスを指定しました。
このように、JSONをどのように変換するのか一つの型に収まらない場合はTypeReference
を使えば良さそうです。