SheepDev

データ形式・構造化データ

YAMLの設定が読み込めない。構文を確認したい

使うツール

設定ファイルをコピーして数行足したら、なぜか一つのサービスだけ設定が効かない。タブをスペースへ直してエラーが消えたのに、期待した動作にはならない。YAMLで困るのは、読めない書き方だけではありません。文法上は読めるけれど、自分が思っている階層と違うデータを書いてしまうこともあります。

ここでは実際のサービスへ設定を適用せず、架空のserverとworkerを使って読み取り方を確認します。YAML整形・検証で文法を確認し、必要ならJSON ⇔ YAML変換で構造や値の型を見ます。整形結果をそのまま本番設定へ上書きするのではなく、どこがどの親に属しているか説明できる状態を目指します。

直す前に、問題を小さく切り出す

何百行もある設定でエラーが出たら、まず利用者自身の作業用コピーを作り、問題のある親項目とその子だけを残した例を試します。ただし、子だけを切り取るとインデントの基準が変わり、元の問題を再現できないことがあります。親の行も残す、行頭の空白を見える表示にする、タブとスペースを区別する、という準備が役立ちます。最初から全文を置換してしまうより、原因と修正の対応を確かめやすくなります。

同じ二文字の空白が、設定の所属を変える

例A:timeoutはserverの設定
server:
  host: example.invalid
  timeout: 30
worker:
  enabled: true
例B:timeoutは文書直下の設定
server:
  host: example.invalid
timeout: 30
worker:
  enabled: true

二つの例を一つずつYAML整形・検証へ入れます。どちらも読み取れますが、同じ構造ではありません。例Aはserverの中にtimeoutを置き、例Bはserverと同じ階層へ置いています。エラーが出ないことを確認した後、timeoutがどの親の下へ入っているかまで見てください。行頭の空白は、見た目をそろえる飾りではありません。

違いが見えにくければ、JSON ⇔ YAML変換へ同じ例を入れ、括弧で囲まれた範囲を比べます。例Aではserver.timeout、例Bでは文書直下のtimeoutという関係になります。変換はこの関係を眺める補助であり、元の設定をJSON形式へ移行しなければならないという意味ではありません。利用先がどちらの位置を要求するかは、そのサービスの設定仕様で確認します。

ハイフンを足すと、同じ項目の続きではなくなる

二つのサービスを持つ一覧
services:
  - name: api
    port: 8080
  - name: worker
    port: 8081

この例ではハイフンが二つあり、一覧の要素も二件です。各portは直前のnameと同じ要素に入ります。portの前にもハイフンを足すと、ポートだけを持つ別要素になり、見た目が似た四件の一覧を作ってしまう場合があります。件数が想定どおりか、名前とポートが同じまとまりに入っているかをセットで確認します。

整形ツールは、人が二件にしたかったのか四件にしたかったのかを知りません。読み取れる形なら、その構造のまま整えます。コピーした設定を別の階層へ移すときは、一行だけを動かすのではなく、その項目に属する子の範囲を確認してから移動する必要があります。整形前後を比較するなら、まず期待する要素数をメモしておくと、見落としを減らせます。

falseと"false"は、見た目以上に違う

型を比べる小さな例
enabled: false
label: "false"
count: 3
code: "003"
note: null
empty: ""

JSONへ変換すると、enabledは真偽値、labelは文字列、countは数値、codeは先頭ゼロを持つ文字列として読めます。noteのnullとemptyの空文字列も別の値です。設定画面では似た表示になっても、利用するプログラムが同じ扱いをするとは限りません。特に識別番号の先頭ゼロは、数値として読み取った後から整形しても元に戻せないことがあります。

だからといって、全項目を引用符で囲むのも正解ではありません。trueや30を文字列へ変えれば、利用先が要求する型に合わなくなる可能性があります。引用符はエラーを隠すためではなく、文字列として扱いたい意味を伝えるために使います。YAMLの版や読み取り側の方式にも差があるので、別のツールで読めたという理由だけで、利用先での検証を省略しないようにします。

同じキーを二回書いたら、後ろが勝つ?

拒否される重複キーの例
timeout: 30
timeout: 60

SheepDevでは、この例は重複キーとしてエラーになります。『最後の設定が優先されるだろう』と決め付けず、どちらを残すつもりだったかを確認して一つにします。別ファイルを重ねる仕組みと、一つの文書の同じ場所へ同名キーを二度書くことは別の話です。利用先の設定統合ルールを、この文法上の重複へ勝手に当てはめないようにしてください。

一方、server.timeoutとworker.timeoutのように親が違えば、同じtimeoutという名前でも別の設定です。名前だけを全文検索して二つ見つかったからといって、片方を消すべきではありません。重複を直す作業でも、まず親子関係を確認します。設定の意味を知らずに重複行を削除する一般的な行整理ツールへ渡すと、必要な項目まで失うおそれがあります。

二行の文章が、一行になる理由

二種類の複数行文字列
literal: |
  first line
  second line
folded: >
  first line
  second line

この例をJSONへ変換すると、literalには途中の改行が残り、foldedでは二つの行が空白でつながります。どちらも最後に改行を持つ例です。同じように二行で書かれていても、読み取った文字列が同じとは限りません。通知文、署名対象の文章、コマンドへ渡す文字列などでは、この違いが結果に影響します。

ここで示したのは、同じ深さの普通の二行という小さな条件です。空行や追加の字下げを含む文章まで『>なら全部一行になる』と広げて考えないでください。実際に使う文章を短い例へ切り出し、変換後の改行を確認するほうが確実です。整形して見やすくなったかだけでなく、読み取った文章が意図どおりかを確かめます。

コメントと値の境界も、コピー時に見直す

値に含める記号とコメントを分けた例
color: "#4F46E5"
message: "status: ready"
retries: 3 # calculation example

色コードの#や文章中のコロンを、そのまま設定の区切りやコメントとして読み取らせたくないときは、文字列として明示します。例のretriesの後ろのコメントは、数値3の一部ではありません。説明記事からコピーするときも、コードの一部なのか注釈なのかを確認してください。見た目だけで記号を削ると、色や文章そのものを壊す場合があります。

JSONへ変換すればコメントは通常のデータとして残りません。YAMLの整形と、別形式への変換は、保存される情報が同じではない操作です。説明用コメントに運用上の判断理由を書いているなら、変換結果だけを元ファイルの代わりにしないでください。読むための作業用コピーと、チームで保守する正本を分けて扱うと混乱を防げます。

読める設定から、意図した設定へ

最後に、対象アプリケーションが求めるキー名、値の型、必須項目を確認します。YAMLとして有効でも、timeoutの単位が秒かミリ秒か、portにどの範囲を許すかはYAMLの文法だけでは決まりません。SheepDevは設定をサービスへ送信・適用せず、そのサービス固有の起動検証も行いません。利用先に検証コマンドや安全な試験環境があれば、そこでの確認が次の段階です。

修正内容を伝えるときも、『インデントを直した』だけでは不十分な場合があります。『timeoutを文書直下からserverの子へ戻し、値30は維持した』と説明できれば、構造と値を区別して確認したことが伝わります。エラーが消えたか、親子関係が合っているか、利用先の意味に合っているか。この三段階で見ると、空白に振り回される作業から抜け出しやすくなります。

参考資料

使うツール

使い方・解説 →