はじめに
この記事では、Javaエンジニアが知っておくべき JSON(JavaScript Object Notation) の基礎から実践的な使い方までを1記事でまとめます。
Web APIのやり取りや設定ファイルなど、実務で避けて通れないJSON。「なんとなく使える」から「正しく理解して使える」レベルを目指しましょう。
1. JSONとは
概要
JSON(ジェイソン)は データを表現するためのテキスト形式 です。
- もともとJavaScriptのオブジェクト表記がベース
- 言語に依存しない(Java, Python, Go, etc. どの言語でも使える)
- 軽量で人間にも読みやすい
- Web API のデータ交換フォーマットとして事実上の標準
XMLとの比較
<!-- XML -->
<user>
<name>田中太郎</name>
<age>25</age>
<active>true</active>
</user>
JSON:
{
"name": "田中太郎",
"age": 25,
"active": true
}
| 項目 | JSON | XML |
|---|---|---|
| 可読性 | シンプルで読みやすい | タグが冗長 |
| データサイズ | 小さい | 大きい |
| パース速度 | 速い | 遅い |
| コメント | 書けない | 書ける |
| 主な用途 | Web API、設定ファイル | 設定ファイル、文書 |
2. JSONの構文ルール
データ型
JSONで使えるデータ型は 6種類 だけです。
| データ型 | 例 | 説明 |
|---|---|---|
| 文字列 | "Hello" |
ダブルクォートで囲む(シングル不可) |
| 数値 |
42, 3.14, -10
|
整数・小数(クォート不要) |
| 真偽値 |
true, false
|
小文字のみ(True は不正) |
| null | null |
値がないことを表す |
| オブジェクト | { "key": "value" } |
キーと値のペア(連想配列) |
| 配列 | [1, 2, 3] |
値の順序付きリスト |
オブジェクト(Object)
キーと値のペアを {} で囲みます。
{
"name": "田中太郎",
"age": 25,
"department": "開発部"
}
ルール:
- キーは必ず ダブルクォートで囲む(
'name'やnameは不正) - キーと値は
:で区切る - ペア同士は
,で区切る - 最後のペアの後にカンマを付けない(トレイリングカンマ不可)
配列(Array)
値を [] で囲みます。
{
"languages": ["Java", "Python", "Go"],
"scores": [85, 92, 78],
"mixed": ["text", 42, true, null]
}
ネスト(入れ子)
オブジェクトと配列は自由にネストできます。
{
"company": "株式会社ABC",
"employees": [
{
"name": "田中太郎",
"age": 25,
"skills": ["Java", "SQL"],
"address": {
"prefecture": "東京都",
"city": "千代田区"
}
},
{
"name": "鈴木花子",
"age": 30,
"skills": ["Python", "Docker"],
"address": {
"prefecture": "大阪府",
"city": "大阪市"
}
}
]
}
よくあるJSONの構文エラー
// ❌ NG:シングルクォート
{ 'name': '田中' }
// ❌ NG:キーにクォートなし
{ name: "田中" }
// ❌ NG:トレイリングカンマ
{ "name": "田中", "age": 25, }
// ❌ NG:undefinedはJSONにない
{ "name": undefined }
// ❌ NG:コメントは書けない
{
// これはエラー
"name": "田中"
}
// ✅ OK:正しいJSON
{ "name": "田中", "age": 25 }
3. JavaでJSONを扱うライブラリ
主要ライブラリの比較
| ライブラリ | 特徴 | よく使われる場面 |
|---|---|---|
| Jackson | 最も広く使われる。高機能・高速 | Spring Boot(デフォルト) |
| Gson | Google製。シンプルで学習コストが低い | Android、小規模プロジェクト |
| org.json | 軽量。JDKに近い感覚で使える | ちょっとしたJSON処理 |
| JSON-P/JSON-B | Jakarta EE標準 | Java EE / Jakarta EE環境 |
本記事では最も実務で使われる Jackson と、学習しやすい Gson を中心に解説します。
Maven 依存関係
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
<!-- Gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.11.0</version>
</dependency>
4. Jackson の使い方
基本:JavaオブジェクトとJSONの変換
Java オブジェクト ──シリアライズ──→ JSON文字列
Java オブジェクト ←─デシリアライズ── JSON文字列
JavaBean(POJO)の用意
public class User {
private String name;
private int age;
private String email;
// デフォルトコンストラクタ(Jackson必須)
public User() {}
public User(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
// getter / setter(省略せず書く)
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
シリアライズ(Java → JSON)
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonSerialize {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
User user = new User("田中太郎", 25, "tanaka@example.com");
// オブジェクト → JSON文字列
String json = mapper.writeValueAsString(user);
System.out.println(json);
// → {"name":"田中太郎","age":25,"email":"tanaka@example.com"}
// 整形して出力(Pretty Print)
String prettyJson = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(user);
System.out.println(prettyJson);
// → {
// "name" : "田中太郎",
// "age" : 25,
// "email" : "tanaka@example.com"
// }
}
}
デシリアライズ(JSON → Java)
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonDeserialize {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
String json = """
{
"name": "田中太郎",
"age": 25,
"email": "tanaka@example.com"
}
""";
// JSON文字列 → オブジェクト
User user = mapper.readValue(json, User.class);
System.out.println("名前: " + user.getName()); // 田中太郎
System.out.println("年齢: " + user.getAge()); // 25
System.out.println("メール: " + user.getEmail()); // tanaka@example.com
}
}
リスト(配列)の変換
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
public class JacksonList {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
// Java List → JSON配列
List<User> users = List.of(
new User("田中太郎", 25, "tanaka@example.com"),
new User("鈴木花子", 30, "suzuki@example.com")
);
String json = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(users);
System.out.println(json);
// JSON配列 → Java List
String jsonArray = """
[
{"name": "田中太郎", "age": 25, "email": "tanaka@example.com"},
{"name": "鈴木花子", "age": 30, "email": "suzuki@example.com"}
]
""";
List<User> parsed = mapper.readValue(jsonArray, new TypeReference<List<User>>() {});
parsed.forEach(u -> System.out.println(u.getName() + " (" + u.getAge() + ")"));
}
}
Map として扱う(クラスを作らない場合)
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;
public class JacksonMap {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
String json = """
{
"name": "田中太郎",
"age": 25,
"active": true
}
""";
// JSON → Map
Map<String, Object> map = mapper.readValue(json, Map.class);
System.out.println(map.get("name")); // 田中太郎
System.out.println(map.get("age")); // 25
System.out.println(map.get("active")); // true
}
}
Jackson アノテーション
import com.fasterxml.jackson.annotation.*;
public class Employee {
// JSONのキー名を変更
@JsonProperty("full_name")
private String name;
private int age;
// JSONに含めない
@JsonIgnore
private String password;
// nullのフィールドをJSONに含めない
@JsonInclude(JsonInclude.Include.NON_NULL)
private String nickname;
// 日付フォーマットを指定
@JsonFormat(pattern = "yyyy-MM-dd")
private java.time.LocalDate birthDate;
// getter / setter 省略
}
| アノテーション | 用途 |
|---|---|
@JsonProperty("name") |
JSONのキー名を指定 |
@JsonIgnore |
シリアライズ/デシリアライズから除外 |
@JsonInclude |
条件付きでフィールドを含める |
@JsonFormat |
日付や数値のフォーマット指定 |
@JsonCreator |
デシリアライズ用のコンストラクタ指定 |
@JsonIgnoreProperties |
未知のプロパティを無視 |
未知のフィールドを無視する
// クラスレベルで設定
@JsonIgnoreProperties(ignoreUnknown = true)
public class User {
private String name;
private int age;
// JSONに "email" があってもエラーにならない
}
// または ObjectMapper で全体設定
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
5. Gson の使い方
シリアライズ / デシリアライズ
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class GsonExample {
public static void main(String[] args) {
// 通常のGson
Gson gson = new Gson();
// 整形出力したい場合
Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
User user = new User("田中太郎", 25, "tanaka@example.com");
// シリアライズ(Java → JSON)
String json = prettyGson.toJson(user);
System.out.println(json);
// デシリアライズ(JSON → Java)
String jsonStr = "{\"name\":\"鈴木花子\",\"age\":30,\"email\":\"suzuki@example.com\"}";
User parsed = gson.fromJson(jsonStr, User.class);
System.out.println(parsed.getName()); // 鈴木花子
}
}
リストの変換
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.List;
public class GsonListExample {
public static void main(String[] args) {
Gson gson = new Gson();
String json = "[{\"name\":\"田中\",\"age\":25},{\"name\":\"鈴木\",\"age\":30}]";
// TypeTokenでジェネリクスの型情報を保持
Type listType = new TypeToken<List<User>>() {}.getType();
List<User> users = gson.fromJson(json, listType);
users.forEach(u -> System.out.println(u.getName()));
}
}
Jackson vs Gson 比較
| 項目 | Jackson | Gson |
|---|---|---|
| 変換メソッド |
writeValueAsString / readValue
|
toJson / fromJson
|
| 中心クラス | ObjectMapper |
Gson |
| リスト変換 | TypeReference |
TypeToken |
| デフォルトコンストラクタ | 必須 | 不要 |
| アノテーション | 豊富(@JsonProperty 等) |
少なめ(@SerializedName 等) |
| 速度 | 速い | やや遅い |
| Spring Boot | デフォルト採用 | 別途追加 |
6. ネストしたJSONの処理
ネストしたオブジェクト
{
"name": "田中太郎",
"age": 25,
"address": {
"prefecture": "東京都",
"city": "千代田区",
"zipCode": "100-0001"
}
}
// Address クラス
public class Address {
private String prefecture;
private String city;
private String zipCode;
// getter / setter 省略
}
// User クラス(Addressを持つ)
public class User {
private String name;
private int age;
private Address address;
// getter / setter 省略
}
// 変換
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(json, User.class);
System.out.println(user.getAddress().getCity()); // 千代田区
オブジェクトの配列を含むJSON
{
"department": "開発部",
"members": [
{ "name": "田中太郎", "role": "リーダー" },
{ "name": "鈴木花子", "role": "メンバー" },
{ "name": "佐藤次郎", "role": "メンバー" }
]
}
public class Member {
private String name;
private String role;
// getter / setter
}
public class Department {
private String department;
private List<Member> members;
// getter / setter
}
// 変換
Department dept = mapper.readValue(json, Department.class);
dept.getMembers().forEach(m ->
System.out.println(m.getName() + " - " + m.getRole())
);
7. JSON と Web API
Servlet で JSON を返す
@WebServlet("/api/users")
public class UserApiServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Content-Type を application/json に設定
response.setContentType("application/json; charset=UTF-8");
List<User> users = List.of(
new User("田中太郎", 25, "tanaka@example.com"),
new User("鈴木花子", 30, "suzuki@example.com")
);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(users);
response.getWriter().write(json);
}
}
Servlet で JSON を受け取る(POST)
@WebServlet("/api/users")
public class UserApiServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=UTF-8");
// リクエストボディからJSONを読み取る
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(request.getReader(), User.class);
System.out.println("受信: " + user.getName() + " (" + user.getAge() + ")");
// レスポンスを返す
String responseJson = "{\"status\": \"success\", \"message\": \"ユーザーを登録しました\"}";
response.getWriter().write(responseJson);
}
}
Web APIのJSON設計パターン
成功レスポンス:
{
"status": "success",
"data": {
"id": 1,
"name": "田中太郎",
"age": 25
}
}
エラーレスポンス:
{
"status": "error",
"code": "USER_NOT_FOUND",
"message": "指定されたユーザーが見つかりません"
}
一覧レスポンス(ページング付き):
{
"status": "success",
"data": [
{ "id": 1, "name": "田中太郎" },
{ "id": 2, "name": "鈴木花子" }
],
"pagination": {
"page": 1,
"perPage": 10,
"totalPages": 5,
"totalCount": 42
}
}
8. JSON のファイル読み書き
ファイルに書き出す
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
public class JsonFileWrite {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
User user = new User("田中太郎", 25, "tanaka@example.com");
// ファイルに書き出し(整形あり)
mapper.writerWithDefaultPrettyPrinter()
.writeValue(new File("user.json"), user);
System.out.println("user.json に書き出しました");
}
}
ファイルから読み込む
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
public class JsonFileRead {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
// ファイルから読み込み
User user = mapper.readValue(new File("user.json"), User.class);
System.out.println("名前: " + user.getName());
System.out.println("年齢: " + user.getAge());
}
}
リソースファイルから読み込む
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.InputStream;
public class JsonResourceRead {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
// src/main/resources/data.json を読み込む
InputStream is = JsonResourceRead.class
.getClassLoader()
.getResourceAsStream("data.json");
User user = mapper.readValue(is, User.class);
System.out.println(user.getName());
}
}
9. よくあるエラーと対処法
エラー一覧
| エラー | 原因 | 対処法 |
|---|---|---|
UnrecognizedPropertyException |
JSONにJavaクラスにないフィールドがある |
@JsonIgnoreProperties(ignoreUnknown = true) を付ける |
InvalidDefinitionException |
デフォルトコンストラクタがない | 引数なしコンストラクタを追加 |
MismatchedInputException |
型が一致しない(例:文字列に数値) | JSON側かJava側の型を修正 |
JsonParseException |
JSONの構文エラー | JSONの形式を確認(カンマ、クォート等) |
JsonMappingException |
マッピングの失敗 | フィールド名やネスト構造を確認 |
デバッグのコツ
// 1. まずJSON文字列をそのまま出力して確認する
System.out.println("受信したJSON: " + jsonString);
// 2. まずMapで受け取って中身を確認する
Map<String, Object> map = mapper.readValue(json, Map.class);
System.out.println(map);
// 3. 整形出力で構造を確認する
String pretty = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(mapper.readTree(json));
System.out.println(pretty);
10. 実践チートシート
Jackson 早見表
ObjectMapper mapper = new ObjectMapper();
// ── シリアライズ(Java → JSON)──
String json = mapper.writeValueAsString(obj); // 文字列へ
mapper.writeValue(new File("out.json"), obj); // ファイルへ
String pretty = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(obj); // 整形
// ── デシリアライズ(JSON → Java)──
User user = mapper.readValue(json, User.class); // 文字列から
User user = mapper.readValue(new File("in.json"), User.class); // ファイルから
// ── リスト ──
List<User> list = mapper.readValue(json, new TypeReference<List<User>>() {});
// ── Map ──
Map<String, Object> map = mapper.readValue(json, Map.class);
// ── JsonNode(ツリーモデル)──
JsonNode root = mapper.readTree(json);
String name = root.get("name").asText();
int age = root.get("age").asInt();
JsonNode members = root.get("members"); // 配列ノード
for (JsonNode m : members) {
System.out.println(m.get("name").asText());
}
// ── 設定 ──
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.registerModule(new JavaTimeModule()); // Java 8 日時対応
Gson 早見表
Gson gson = new Gson();
Gson pretty = new GsonBuilder().setPrettyPrinting().create();
// ── シリアライズ ──
String json = gson.toJson(obj);
// ── デシリアライズ ──
User user = gson.fromJson(json, User.class);
// ── リスト ──
Type type = new TypeToken<List<User>>() {}.getType();
List<User> list = gson.fromJson(json, type);
// ── JsonElement(ツリーモデル)──
JsonElement element = JsonParser.parseString(json);
JsonObject obj = element.getAsJsonObject();
String name = obj.get("name").getAsString();
練習問題
問題1:基本的なシリアライズ/デシリアライズ ⭐
以下のJSONを Product クラスにデシリアライズし、価格を10%引きにしてから再度JSONに変換して出力してください。
{
"name": "ノートPC",
"price": 98000,
"category": "電子機器"
}
模範解答
import com.fasterxml.jackson.databind.ObjectMapper;
class Product {
private String name;
private int price;
private String category;
public Product() {}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getPrice() { return price; }
public void setPrice(int price) { this.price = price; }
public String getCategory() { return category; }
public void setCategory(String category) { this.category = category; }
}
public class Exercise01 {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
String json = """
{ "name": "ノートPC", "price": 98000, "category": "電子機器" }
""";
// デシリアライズ
Product product = mapper.readValue(json, Product.class);
System.out.println("元の価格: " + product.getPrice() + "円");
// 10%引き
product.setPrice((int)(product.getPrice() * 0.9));
System.out.println("割引後: " + product.getPrice() + "円");
// シリアライズ
String result = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(product);
System.out.println(result);
}
}
問題2:ネストしたJSONの処理 ⭐⭐
以下のJSONを適切なクラス構成でデシリアライズし、全社員の名前と部署を出力してください。
{
"company": "株式会社ABC",
"departments": [
{
"name": "開発部",
"employees": [
{ "name": "田中太郎", "age": 25 },
{ "name": "鈴木花子", "age": 30 }
]
},
{
"name": "営業部",
"employees": [
{ "name": "佐藤次郎", "age": 28 }
]
}
]
}
期待する出力:
会社名: 株式会社ABC
[開発部] 田中太郎(25歳)
[開発部] 鈴木花子(30歳)
[営業部] 佐藤次郎(28歳)
模範解答
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
@JsonIgnoreProperties(ignoreUnknown = true)
class Employee {
private String name;
private int age;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class Department {
private String name;
private List<Employee> employees;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public List<Employee> getEmployees() { return employees; }
public void setEmployees(List<Employee> employees) { this.employees = employees; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class Company {
private String company;
private List<Department> departments;
public String getCompany() { return company; }
public void setCompany(String company) { this.company = company; }
public List<Department> getDepartments() { return departments; }
public void setDepartments(List<Department> departments) { this.departments = departments; }
}
public class Exercise02 {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
String json = """
{
"company": "株式会社ABC",
"departments": [
{
"name": "開発部",
"employees": [
{ "name": "田中太郎", "age": 25 },
{ "name": "鈴木花子", "age": 30 }
]
},
{
"name": "営業部",
"employees": [
{ "name": "佐藤次郎", "age": 28 }
]
}
]
}
""";
Company company = mapper.readValue(json, Company.class);
System.out.println("会社名: " + company.getCompany());
for (Department dept : company.getDepartments()) {
for (Employee emp : dept.getEmployees()) {
System.out.println("[" + dept.getName() + "] " + emp.getName() + "(" + emp.getAge() + "歳)");
}
}
}
}
問題3:JSONツリーモデルでの処理 ⭐⭐
以下のJSONをクラスを作らずに JsonNode(ツリーモデル)で処理し、各商品の税込価格(10%)を計算して出力してください。
{
"store": "ABCストア",
"products": [
{ "name": "りんご", "price": 150 },
{ "name": "バナナ", "price": 100 },
{ "name": "みかん", "price": 200 }
]
}
期待する出力:
店舗: ABCストア
りんご: 150円 → 税込 165円
バナナ: 100円 → 税込 110円
みかん: 200円 → 税込 220円
模範解答
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Exercise03 {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
String json = """
{
"store": "ABCストア",
"products": [
{ "name": "りんご", "price": 150 },
{ "name": "バナナ", "price": 100 },
{ "name": "みかん", "price": 200 }
]
}
""";
JsonNode root = mapper.readTree(json);
String store = root.get("store").asText();
System.out.println("店舗: " + store);
JsonNode products = root.get("products");
for (JsonNode product : products) {
String name = product.get("name").asText();
int price = product.get("price").asInt();
int taxIncluded = (int)(price * 1.10);
System.out.println(name + ": " + price + "円 → 税込 " + taxIncluded + "円");
}
}
}
ポイント: JsonNode(ツリーモデル)はクラスを作らずにJSONを操作したいときに便利です。探索的にデータを確認したり、構造がよく変わるJSONを扱う場合に使います。
まとめ
| テーマ | キーワード |
|---|---|
| JSONの基本 | オブジェクト {}、配列 []、6つのデータ型 |
| 構文ルール | ダブルクォート必須、トレイリングカンマ不可 |
| Jacksonの基本 |
ObjectMapper、writeValueAsString、readValue
|
| Gsonの基本 |
Gson、toJson、fromJson
|
| アノテーション |
@JsonProperty、@JsonIgnore、@JsonFormat
|
| リスト変換 |
TypeReference(Jackson)、TypeToken(Gson) |
| ネスト処理 | クラスのネスト構成で自動マッピング |
| ツリーモデル |
JsonNode(Jackson)、JsonElement(Gson) |
| Web API |
application/json、リクエスト/レスポンス |
| ファイル操作 |
writeValue(File)、readValue(File)
|
著者: @kotaro_ai_lab
AI駆動開発やテック情報を毎日発信しています。フォローお気軽にどうぞ!