概要
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 として取り出して使用する
コード例
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));
}
}Version Coverage
テキストブロックで YAML サンプル文字列をコード内に読みやすく埋め込める。var による型推論で記述量が減る。SnakeYAML との組み合わせで record にマッピングすることも可能。
// 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)にしか対応しない。ネスト構造やリスト、アンカー・エイリアスが必要な場合は SnakeYAML を使うこと
SnakeYAML の yaml.load(untrustedInput) はデシリアライズ攻撃のリスクがある。外部入力を扱う場合は SafeConstructor を使うか、yaml.load に型パラメータを指定する
YAML のインデントはスペースのみ許容される(タブは不可)。設定ファイルの編集時にエディタのタブ設定を確認すること
YAML の値として yes/no/on/off を書くと Boolean として解釈される場合がある。文字列として扱いたい場合は引用符で囲む必要がある
FAQ
フラットな key=value で済むなら Properties のほうが標準 API で完結します。ネスト構造やリストが必要な場合、可読性を重視する場合に YAML を選ぶのが合理的です。
信頼できない入力に対して yaml.load を使うと任意のクラスをインスタンス化される危険があります。SafeConstructor を使うか、new Yaml(new Constructor(Map.class)) で型を限定してください。
SnakeYAML は YAML 1.1 準拠で、yes/no/on/off を Boolean に自動変換します。文字列として扱いたい場合は引用符で囲む必要があります。YAML 1.2 対応が必要なら SnakeYAML Engine を検討してください。