概要

YAML は Docker Compose、Kubernetes、Spring Boot の application.yml など、インフラや設定ファイルの記述フォーマットとして広く使われています。しかし Java 標準 API には YAML パーサーが含まれておらず、「ちょっとした設定を読みたいだけなのに外部ライブラリが必要なのか」という疑問に突き当たります。この記事では、フラットな key: value 形式に限定した簡易 YAML パーサーを Pure Java で実装し、コメント行のスキップ、引用符の除去、Map への変換までを扱います。さらに、ネスト構造やリストが必要な場合に SnakeYAML をどう使うかも併せて紹介し、「どこまで自前で対応し、どこからライブラリに委ねるか」の判断材料を提供します。

使いどころ

社内ツールの簡易設定ファイル(host・port・DB接続先など)をフラットな YAML で管理し、起動時に読み込む

CI/CD パイプラインの設定値をアプリケーション側で読み取り、環境ごとの挙動を切り替える

テストデータの定義を YAML で記述し、テストコードから Map として取り出して使用する

コード例

YamlParser.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 形式のみ対応)。
 * ネスト構造やリストが必要な場合は SnakeYAML を使用してください。
 */
public class YamlParser {

    /** フラット形式の YAML 文字列を Map に変換する */
    public static Map<String, String> parseFlat(String yaml) throws IOException {
        var result = new LinkedHashMap<String, String>();
        var 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) {
                var key = line.substring(0, colonIdx).trim();
                var value = line.substring(colonIdx + 2).trim();
                // 引用符を除去
                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) {
        var sb = new StringBuilder();
        for (var 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 文字列を解析
        var yaml = """
                # アプリケーション設定
                app.name: java-recipes
                app.version: 1.0.0
                server.host: localhost
                server.port: 8080
                database.url: jdbc:postgresql://localhost/mydb
                """;

        var config = parseFlat(yaml);
        System.out.println("=== 解析結果 ===");
        config.forEach((k, v) -> System.out.println(k + " = " + v));

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

Java 8 / 17 / 21 の完全なサンプルコードは GitHub リポジトリ で確認できます。

Version Coverage

テキストブロックで YAML サンプル文字列をコード内に読みやすく埋め込める。var による型推論で記述量が減る。SnakeYAML との組み合わせで record にマッピングすることも可能。

Java 17
// Java 17: テキストブロックで YAML を見やすく記述
var yamlStr = """
    app.name: my-app
    server.host: localhost
    server.port: 8080
    """;
var config = parseFlat(yamlStr);
// var で型推論、記述が簡潔に

Library Comparison

Pure Java(自前パーサー)フラットな key: value 形式のみで十分な場合。設定項目が少なく、ライブラリ依存を増やしたくないとき。ネスト構造・リスト・アンカーなど YAML の高度な機能には対応できない。複雑な設定が増えた時点で限界が来る。
SnakeYAMLネスト構造やリストを含む YAML を扱う場合。Spring Boot の application.yml をアプリ側で直接読み取りたいとき。外部依存が増える。yaml.load の安全性(デシリアライズ攻撃)に注意が必要。
Jackson YAML(jackson-dataformat-yaml)JSON と YAML を同じ ObjectMapper 体系で扱いたい場合。既に Jackson を使っているプロジェクトで統一したいとき。Jackson 本体 + YAML モジュールの依存が増える。小規模な設定読み込みには過剰なケースが多い。

注意点

Pure Java の簡易実装はフラット形式(key: value)にしか対応しない。ネスト構造やリスト、アンカー・エイリアスが必要な場合は SnakeYAML を使うこと

SnakeYAML の yaml.load(untrustedInput) はデシリアライズ攻撃のリスクがある。外部入力を扱う場合は SafeConstructor を使うか、yaml.load に型パラメータを指定する

YAML のインデントはスペースのみ許容される(タブは不可)。設定ファイルの編集時にエディタのタブ設定を確認すること

YAML の値として yes/no/on/off を書くと Boolean として解釈される場合がある。文字列として扱いたい場合は引用符で囲む必要がある

FAQ

Properties ファイルと YAML のどちらを選ぶべきですか。

フラットな key=value で済むなら Properties のほうが標準 API で完結します。ネスト構造やリストが必要な場合、可読性を重視する場合に YAML を選ぶのが合理的です。

SnakeYAML の yaml.load は安全ですか。

信頼できない入力に対して yaml.load を使うと任意のクラスをインスタンス化される危険があります。SafeConstructor を使うか、new Yaml(new Constructor(Map.class)) で型を限定してください。

YAML 1.1 と 1.2 の違いは意識すべきですか。

SnakeYAML は YAML 1.1 準拠で、yes/no/on/off を Boolean に自動変換します。文字列として扱いたい場合は引用符で囲む必要があります。YAML 1.2 対応が必要なら SnakeYAML Engine を検討してください。

関連書籍

この記事のテーマをさらに深く学びたい方へ。

※ Amazon アソシエイトリンクを含みます