java-recipes

ホーム ファイルI/O › F-05: YAML の読み書き

F-05: YAML の読み書き(SnakeYAML)

YAML はアプリケーションの設定ファイルとして広く使われるフォーマットです。 Java 標準ライブラリには YAML パーサーが含まれていないため、 実務では SnakeYAML を使うのが一般的です。 Java 8 タブでは Pure Java による簡易パーサー(フラット形式のみ)も紹介します。

YAML の基本構文

スカラー(キーと値)

name: 山田太郎
age: 30

ネスト(マッピング)

server:
  host: localhost
  port: 8080

リスト(シーケンス)

fruits:
  - りんご
  - バナナ

Maven 依存定義(SnakeYAML)

<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>2.2</version>
</dependency>

サンプルコード

Sample.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Pure Java による簡易 YAML パーサー(フラット key: value 形式のみ対応)。
 * ネスト構造・リスト・アンカー等には対応していません。
 * 複雑な YAML を扱う場合は SnakeYAML を使用してください(Java 17+ タブ参照)。
 */
public class YamlSample {

    /**
     * フラット形式の YAML 文字列を解析して Map に変換する。
     * コメント行(# で始まる行)・空行はスキップする。
     */
    public static Map<String, String> parseFlat(String yaml) throws IOException {
        Map<String, String> result = new LinkedHashMap<>();
        BufferedReader reader = new BufferedReader(new StringReader(yaml));
        String line;
        while ((line = reader.readLine()) != null) {
            line = line.trim();
            if (line.isEmpty() || line.startsWith("#")) {
                continue; // 空行・コメントをスキップ
            }
            int colonIdx = line.indexOf(": ");
            if (colonIdx > 0) {
                String key = line.substring(0, colonIdx).trim();
                String value = line.substring(colonIdx + 2).trim();
                // 引用符を除去("value" や 'value' → value)
                if ((value.startsWith("\"") && value.endsWith("\""))
                        || (value.startsWith("'") && value.endsWith("'"))) {
                    value = value.substring(1, value.length() - 1);
                }
                result.put(key, value);
            }
        }
        return result;
    }

    /**
     * Map を YAML 文字列として出力する(フラット形式)。
     */
    public static String dumpFlat(Map<String, String> data) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : data.entrySet()) {
            sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
        }
        return sb.toString();
    }

    public static void main(String[] args) throws IOException {

        // ① YAML 文字列を解析(フラット形式)
        String yaml =
            "# アプリケーション設定\n" +
            "app.name: java-recipes\n" +
            "app.version: 1.0.0\n" +
            "server.host: localhost\n" +
            "server.port: 8080\n";

        Map<String, String> config = parseFlat(yaml);
        System.out.println("=== 解析結果 ===");
        for (Map.Entry<String, String> entry : config.entrySet()) {
            System.out.println(entry.getKey() + " = " + entry.getValue());
        }

        // ② Map を YAML として出力
        Map<String, String> newConfig = new LinkedHashMap<>();
        newConfig.put("app.name", "my-app");
        newConfig.put("server.port", "443");
        System.out.println("\n=== YAML 出力 ===");
        System.out.println(dumpFlat(newConfig));

        System.out.println("注意: この Pure Java 実装はフラット形式のみ対応です。");
        System.out.println("ネスト構造が必要な場合は SnakeYAML を使用してください。");
    }
}

よくあるミス・注意点

⚠️ インデントはスペースのみ(タブ文字は使えない)

YAML の仕様ではインデントにタブ文字を使えません。 エディタの設定によっては Tab キーがタブ文字を入力するため注意が必要です。 必ずスペース(通常2文字)でインデントしてください。 タブが混入すると SnakeYAML が ScannerException をスローします。

📌 デシリアライズの型キャストに注意

yaml.load() Object(または Map<String, Object>)を返します。 数値フィールドは Integer Long として返るため、String にキャストしようとすると ClassCastException が発生します。

📌 実務では設定ファイルの読み込みに使うことが多い

Spring Boot などのフレームワークでは application.yml の読み込みに SnakeYAML が内部で使われています。 フレームワークを使わない Pure Java アプリで設定ファイルを YAML にしたい場合に直接使用します。

テストする観点

  • シリアライズしてデシリアライズすると元のデータと一致するか(ラウンドトリップテスト)
  • ネストした YAML のキーにアクセスできるか
  • リスト要素が正しい順序で読み込まれるか
  • コメント行(# で始まる行)が無視されるか
  • インデントにタブが混入した YAML で例外が発生するか
  • 数値フィールドが Integer 型として読み込まれるか

GitHub でソースコードを見る →