1 分鐘重點 — YAML 縮排與語法
- 縮排只使用空白(空格),不得使用 Tab。2 格空格是事實上的標準。
key: value中冒號後方必須有空格。key:value會被視為單一的字串。- 列表使用
-(減號加空格),Mapping 使用key: value。 - 可能被誤判為數字、布林值、日期的值要加引號:
version: "1.0"。 - 想快速檢查錯誤,把 YAML 貼到 FormatArc 的 YAML to JSON 轉換器 就能看到附帶行號的錯誤訊息。
YAML 是什麼、用在哪裡
YAML(YAML Ain't Markup Language)是一種以人為本的純文字結構化資料格式。它涵蓋與 JSON 相同的資料模型——字串、數字、布林值、null、列表、Mapping——但語法不靠括號,而是以縮排(Indentation)表達層級結構。
如果你在做 DevOps 或雲端原生開發,應該已經接觸過 YAML:
- Kubernetes manifest、Helm chart
- Docker Compose 檔案
- GitHub Actions、GitLab CI、CircleCI 的 workflow
- Ansible playbook
- OpenAPI 規格
- 靜態網站產生器(Hugo、Jekyll、Eleventy)
如果你的 YAML 是用在 Markdown 檔案開頭的 frontmatter,YAML 轉 JSON 時有幾個特有的陷阱,詳見 Markdown frontmatter 格式與語法。
這篇教學會用具體範例與新手常犯的錯誤清單,完整講解你實際會用到的 YAML 語法。想先了解 YAML 的概說,可看 YAML 是什麼;想比較 YAML 與 JSON 的差異,見 YAML JSON 比較;YAML 轉 JSON 的步驟則見 YAML 轉 JSON 指南。
YAML 的三個基本元素
YAML 檔案中出現的所有資料,都是以下三種之一:
- 標量(Scalar)— 字串、數字、布林值、null 等單一值
- 序列(Sequence)— 有順序的列表
- 映射(Mapping)— 鍵值對
這三種元素可以任意巢狀。Mapping 裡可以放列表,列表裡也可以放 Mapping。僅憑這些結構就能表達所有 JSON 相容的資料。
縮排規則
YAML 以縮排表達巢狀層級。需要記住的規則如下:
- 只使用空格,不得使用 Tab 字元。Tab 在多數解析器中會觸發語法錯誤。
- 一旦決定縮排寬度,整個檔案保持一致(通常是 2 格空格)。
- 同一層級的項目必須縮排到相同的深度。
- 沒有結尾括號,結構完全由縮排決定。
正確的範例:
database:
host: localhost
port: 5432
credentials:
user: admin
password: secret
database 是一個 Mapping,包含 host、port、credentials。credentials 又是巢狀的 Mapping。用 2 格縮排就能一眼看清整體結構。
鍵值對(Mapping)的寫法
Mapping 以 key: value 的形式書寫。冒號後方必須有空格,key:value 省略空格的話會被當成單一的字串而非 Mapping。
name: Alice
age: 30
is_admin: true
bio: null
鍵通常是不含空格的簡單字串,但加上引號後也可以包含空格:
"full name": Alice Cooper
值可以是 YAML 支援的任何型別:標量、序列、Mapping。
列表(Sequence)的寫法
序列是在行首加上 - (減號加空格)來表示:
fruits:
- apple
- banana
- cherry
列表中可以巢狀 Mapping:
users:
- name: Alice
role: admin
- name: Bob
role: editor
每個減號標示一個新項目開始,到下一個同層減號為止的內容都屬於該項目。
區塊格式與流程格式
前面看到的寫法都是區塊格式(Block style),一行一個項目,用縮排表示巢狀。YAML 也支援類似 JSON 的行內流程格式(Flow style):
fruits: [apple, banana, cherry]
users: [{name: Alice, role: admin}, {name: Bob, role: editor}]
流程格式適合短列表,但資料一多可讀性會急遽下降。實務檔案建議以區塊格式為主。流程格式的 [ 沒有關閉的話,各解析器回報的錯誤也不同(詳見後面的解析器錯誤對照表)。
字串與引號:何時需要加引號
YAML 的字串大部分時候可以不加引號直接寫:
greeting: Hello, world
需要加引號的情況:
- 值會被誤判為其他型別:
version: "1.0"、country: "NO"、postal_code: "07030" - 值以 YAML 結構字元開頭:
[、]、{、}、,、*、|、>、%、@、`、"、'、#、&、!。開頭的-、:、?只有在緊接空格時才有問題。 - 值中含有冒號加空格(
:)的情形(會被誤判為 Mapping) - 想使用
\n或\t等跳脫序列
YAML 同時支援單引號(')與雙引號(")。雙引號會解析跳脫序列,單引號則保留原文。
escaped: "line1\nline2"
literal: 'line1\nline2'
實際會造成問題的字元可分為兩組,所屬的組別決定是否需要引號。下表是本站轉換工具所使用的解析器(yaml (eemeli) 2.8.3,YAML.parse 預設設定)的行為(2026-08-15 實測,重現腳本在儲存庫 scripts/benchmarks/yaml-quote-requirements/)。中間欄寫「安全」表示該字元不在值開頭時可以不加引號。
| 字元 | 放在值開頭 | 放在值中間 | 單引號是否足夠 | 雙引號 |
|---|---|---|---|---|
[ ] { } , | 解析錯誤 | 安全 | 足夠 | 可用 |
* | 解析錯誤(被當作別名) | 安全 | 足夠 | 可用 |
& | 不報錯,被當成錨點,值消失 | 安全 | 足夠 | 可用 |
! | 不報錯,被當成 tag | 安全 | 足夠 | 可用 |
# | 不報錯,整段值被當作註解,變 null | 前面無空格時安全 | 足夠 | 可用 |
| > | 解析錯誤(被當作區塊標量) | 安全 | 足夠 | 可用 |
% @ ` | 解析錯誤(保留字元) | 安全 | 足夠 | 可用 |
- ? | 單獨安全,緊接空格時解析錯誤 | 安全 | 足夠 | 可用 |
: | 單獨安全,緊接空格時解析錯誤 | 緊接空格時無論位置都會解析錯誤 | 足夠 | 可用 |
" | 解析錯誤 | 安全 | 足夠 | 需以 \" 跳脫 |
' | 解析錯誤 | 安全 | 不夠。需以 '' 重複表示 | 較穩妥 |
從表中可以得出兩點結論。第一,值中間危險的只有 : 和 #,所以 url: http://example.com/a,b 不加引號也安全,其餘都是開頭字元的問題。第二,單引號除了單引號字元本身外幾乎都能安全包裹,這是在貼上 Windows 路徑或正規表示式時建議用單引號的原因。只有當你需要 \n 或 \t 被解釋為真正的換行或 Tab 時才使用雙引號。
模板語法 {{ }} 放在值開頭會壞掉
Helm 模板或 Ansible 變數展開在未經渲染前直接交給 YAML 解析器,會踩到 { 被視為特殊字元的陷阱:
# 未渲染的 Helm 模板
image: {{ .Values.image }}
{{ 會被解析為兩層巢狀的 flow mapping,而且失敗的方式因解析器而異。實測結果(2026-07-12,重現腳本在儲存庫 scripts/benchmarks/yaml-syntax-errors/)顯示 PyYAML 與 ruamel.yaml 會以 found unhashable key 錯誤中斷,但 js-yaml 與 eemeli/yaml 完全不報錯,靜默回傳 {"image": {"[object Object]": null}} 這種壞掉的物件。不報錯反而更危險,問題要到管線後段才會浮現。
解決方法有兩種:將整段值加引號使其成為字串(image: "{{ .Values.image }}"),或只在模板渲染完成後才驗證 YAML。另外 GitHub Actions 的 ${{ }} 以 $ 開頭,不會被解析為 flow mapping,所以不會遇到這個問題。
多行字串
長文字區塊有兩種寫法。
Literal block(|)保留原始換行:
description: |
這是第一行。
這是第二行。
空行後的第四行。
Folded scalar(>)把換行折成空格,適合在來源中折行書寫的長段落:
paragraph: >
這句長句在來源中拆成
好幾行書寫,但 YAML
會把它折成一行處理。
Chomp 指標:strip、clip、keep
YAML 的區塊標量有一個 chomp 指標,決定尾部換行如何處理。指標寫在 | 或 > 的緊後面。
| 指標 | 行為 | 範例 |
|---|---|---|
| (無)— clip | 保留最後 1 個換行。預設值。 | | |
- — strip | 移除所有尾部換行。 | |- >- |
+ — keep | 保留所有尾部空行。 | |+ >+ |
clip: |
line one
line two
strip: |-
line one
line two
keep: |+
line one
line two
折疊格式(>、>-、>+)也適用同樣的三種模式。|- 適合嵌入 JSON 或環境變數時不想要尾部換行的場合。|+ 適合產生 shell script 時尾部空行有意義的場合。
型別與解析器的型別推論
YAML 會從未加引號的值自動推斷型別。同樣的字面值,因解析器實現的 YAML 版本與使用的 schema 不同,可能被解讀為整數、浮點數、日期或字串。
integer: 42
hex: 0xFF # 255(整數,16 進位)
octal: 0o17 # 15(整數,8 進位,YAML 1.2)
octal_legacy: 0644 # 420(整數,8 進位,YAML 1.1)— 檔案權限的陷阱
float: 3.14
negative: -7
exponential: 1e3 # 1000.0(浮點數,指數表示法)
infinity: .inf # +Infinity(浮點數)
negative_infinity: -.inf
not_a_number: .nan # NaN(浮點數)
boolean_true: true
boolean_false: false
null_value: null
tilde_null: ~
date: 2026-04-13
timestamp: 2026-04-13T09:30:00Z
這些值加上引號後都可以固定為字串:
version_string: "1.0" # 不是數字 1.0
zip_code: "07030" # 不是數字 7030
file_mode: "0644" # 不是 8 進位整數
下表整理未加引號的字面值在現代 YAML 1.2 Core schema 的 loader(PyYAML、js-yaml、SnakeYAML 預設)與 YAML 1.1 的 loader(舊版 PyYAML、Symfony YAML)中分別會被解讀成什麼。多數出人意料的行為就藏在這兩欄的差異中。
| 字面值 | YAML 1.2 Core | YAML 1.1 | 注意事項 |
|---|---|---|---|
42 | integer (42) | integer (42) | — |
0xFF | integer (255) | integer (255) | 16 進位解析是規格定義的行為 |
0o17 | integer (15) | (不識別) | YAML 1.2 限定 |
0644 | integer (644) | integer (420, 8 進位) | 檔案權限解讀因版本而異 |
1e3 | float (1000.0) | float (1000.0) | 部分工具顯示時會省略 .0 |
.inf / -.inf | float (±Infinity) | float (±Infinity) | 無法序列化為 JSON |
.nan | float (NaN) | float (NaN) | 無法序列化為 JSON |
2026-04-13 | string | date(日期物件) | JSON 與 YAML 來回轉換時型別會變 |
2026-04-13T09:30Z | string | timestamp | 同樣的問題 |
true / false | boolean | boolean | — |
yes / no / on / off | string | boolean | 「Norway problem」 |
NO | string | boolean (false) | 下一節詳細說明 |
~ / null / Null / NULL | null | null | 大小寫皆解讀為 null |
1.0 | float (1.0) | float (1.0) | 部分工具會重新序列化為 1 |
如果你的解析器接近正式環境,最右欄的項目都應視為潛在 bug。確認手邊 loader 使用哪個版本最快的方法,是把具代表性的檔案貼到 FormatArc 的 YAML to JSON 轉換器 看 JSON 輸出。"NO" 還是 false、"2026-04-13" 還是 ISO 日期字串,一目瞭然。
兩欄差異的根源在於 YAML 1.1 與 1.2 對型別推論的定義不同。YAML 1.1 包含廣泛的隱含型別 tag,包括時間戳記、60 進位(sexagesimal)、以及導致 Norway problem 的布林值詞彙。YAML 1.2 改用更嚴格的 Core schema在新分頁中開啟,布林值限定為 true 與 false(規格的 Core 正規表示式也接受首字母大寫或全大寫的 True/TRUE/False/FALSE,但不再接受 yes/no/on/off),取消隱含時間戳記與 60 進位 tag,其餘則跟隨 JSON 的值型別。完整的正規表示式集見 YAML 1.2.2 規格在新分頁中開啟。當兩個工具對未加引號的值的解讀產生分歧時,原因幾乎必定是一方實現 1.1 的型別規則,另一方實現 1.2 的 Core schema。
Norway problem — NO 變成 false 的陷阱
YAML 最著名的陷阱就是「Norway problem」。YAML 1.1 的解析器(PyYAML、Symfony YAML、舊版 SnakeYAML 等至今仍廣泛使用)會把未加引號的 NO 解讀為布林值 false。國家代碼列表如果這樣寫:
countries:
- DE
- FR
- NO
- SE
解析後就會變成 ["DE", "FR", false, "SE"]。YES、ON、OFF、Y、N 以及大小寫變體也會依解析器不同觸發同樣的問題。
解決方法就是對可能被誤讀的值加上引號:
countries:
- "DE"
- "FR"
- "NO"
- "SE"
YAML 1.2 雖然把布林值限定為 true / false,但實務上多數工具仍然內嵌 1.1 時代的 loader。國家代碼、語言代碼、版本字串、短標識符都應該加上引號。想確認解析器相容性的話,把設定貼到 FormatArc 的 YAML to JSON 轉換器 就能立刻看到 "NO" 是否保留為字串。
嚴格的現代布林值集合見 YAML 1.2.2 規格在新分頁中開啟,舊工具實際實現的規則見 YAML 1.1 規格在新分頁中開啟。
註解
註解從 # 開始到行末為止。可以獨立成一行,也可以跟在值後面:
# 上游請求的最大重試次數
retries: 3 # 再大就會超過上游 timeout
註解是 YAML 相比 JSON 在設定檔上最大的優勢之一。與其寫「這是什麼」,不如寫「為什麼設這個值」。轉成 JSON 後所有註解都會消失,所以建議以 YAML 作為來源檔管理。如果還是需要在 JSON 中保留註解,可看 JSON 註解方式 的 4 種替代方法。
錨點與別名:重複利用
YAML 可以用 & 定義錨點(Anchor),用 * 引用別名(Alias),並以 <<: 合併鍵(Merge key)把 Mapping 作為預設值引入。
defaults: &defaults
adapter: postgres
host: db.internal
pool: 5
development:
<<: *defaults
database: myapp_dev
production:
<<: *defaults
database: myapp_prod
pool: 20
production 繼承 defaults 的設定,只覆寫 pool 的值。不用複製貼上就能在各環境間共用設定。
合併鍵(<<:)共用設定
<<: 合併鍵是錨點最好用的搭配。它把一個 Mapping 的鍵引入另一個 Mapping,讓「跟那個一樣但只有這裡要覆寫」的結構不用重複寫。Docker Compose、GitLab CI、Rails 的 database.yml 都常見這個模式。
base: &base
image: node:20
restart: unless-stopped
environment:
NODE_ENV: production
services:
api:
<<: *base
command: npm run start:api
worker:
<<: *base
command: npm run start:worker
environment:
NODE_ENV: production
WORKER_QUEUE: high
使用時有兩點要注意:
- 合併鍵只做 1 層合併(shallow merge)。巢狀 Mapping(如上方的
environment)不會做深合併(deep merge),而是整體覆寫。 - 合併鍵是 YAML 1.1 的規格功能。嚴格的 YAML 1.2 解析器可能忽略它,但 Docker Compose 與 GitLab CI 仍支援 1.1 語意所以不會出問題。
Schema 與 tag:YAML 如何決定型別
YAML 1.2 定義了三個控制未加引號字面值解讀方式的 schema:
- FailSafe — 只支援字串、Mapping、Sequence。最安全的 schema,其餘一律視為字串。很少被設為預設。
- JSON — 支援 JSON 相容型別(字串、整數、浮點數、布林值、null、Mapping、Sequence),與
JSON.parse產出一致。 - Core — 一般預設。在 JSON 基礎上加上前述表中的型別推論規則(16 進位、8 進位、
.inf、.nan、~)。
多數解析器預設搭載 Core(PyYAML 的 safe_load、js-yaml 預設值、SnakeYAML 的 SafeConstructor)。Symfony YAML 則仍以 YAML 1.1 語意為預設。
當 schema 推斷出錯——例如你打算寫字串但 version: 1.0 被解析為浮點數——可以用明確的 tag 強制型別:
version: !!str 1.0 # 強制為字串 "1.0"
count: !!int "42" # 即使加了引號也強制為整數 42
empty: !!null "" # 空字串改為明確的 null
!! 前綴表示使用預設 tag 庫的標準 tag。自訂 tag 用單一 !(例如 AWS CloudFormation 的 !Ref),需要解析器端有對應的實作。應用程式層的 YAML 檔案通常只要認識 !!str 就能處理絕大部分型別陷阱。
多文件(Multi-document)
一個 YAML 檔案可以包含多份獨立文件,以 --- 獨立行作為分隔:
---
kind: Service
name: web
---
kind: Deployment
name: web
replicas: 3
這是 Kubernetes 把多份 manifest 包在同一個檔案時的標準格式。JSON 沒有對應的結構,轉換時必須選擇其中一份或用外層陣列包起來。
錨點、合併鍵、多文件在各平台的相容性(實測)
錨點、合併鍵、多文件在 YAML 語法上都是合法的,但能不能用完全取決於你貼到哪裡。4 種解析器與 Docker Compose 為直接實測(2026-07-12,重現腳本在儲存庫 scripts/benchmarks/yaml-tool-acceptance/),託管平台則查官方文件。
| 環境 | 錨點 & * | 合併鍵 <<: | 多文件 --- |
|---|---|---|---|
| js-yaml | 支援(實測) | 支援(實測) | load 會報錯,loadAll 支援(實測) |
| PyYAML | 支援(實測) | 支援(實測) | safe_load 會報錯,safe_load_all 支援(實測) |
| yaml (eemeli)(FormatArc 轉換器使用的解析器) | 支援(實測) | 預設不展開,<< 保留為字面鍵(實測) | parse 會報錯,parseAllDocuments 支援(實測) |
| Docker Compose v5.3.0 | 支援(實測) | 支援(實測) | 多份文件合併為一份配置(實測) |
| GitHub Actions | 2025-09-18 起支援在新分頁中開啟 | 不支援 — 使用會語法錯誤在新分頁中開啟 | 官方文件未提及 |
| GitLab CI | 支援在新分頁中開啟 | 支援在新分頁中開啟 | 官方文件未提及 |
| Kubernetes (kubectl) | 官方文件未明確說明 | 官方文件未明確說明 | 支援在新分頁中開啟 |
這張表有三個重點:
- GitHub Actions 支援錨點但不支援合併鍵。2025 年 9 月加入錨點支援後
<<:仍是語法錯誤,所以 GitLab CI 的 workflow 不能直接搬過來。 - eemeli/yaml 預設遵循 YAML 1.2,因此 YAML 1.1 規格的合併鍵不會被自動展開。不報錯反而危險,
{"<<": {...}}這個鍵會靜默保留,變成不预期的 bug。 - 多文件取決於使用哪個 API 函式。4 種解析器在單文件讀取 API 中都會報錯,改用多文件專用 API 就正常。
實際使用 YAML 的三個場景
無論幾行的設定檔還是大規模部署 manifest,適用的語法規則都一樣。以下是 2026 年開發中 YAML 出現頻率最高的三個場景。
Kubernetes Deployment manifest
最小化的 Deployment 就同時用到 Mapping、Sequence、多行字串、布林值。spec.template.spec.containers 附近的縮排錯誤是最常見的失敗模式。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app: web
tier: frontend
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27-alpine
ports:
- containerPort: 80
env:
- name: FEATURE_FLAG_NEW_HEADER
value: "true" # 故意加引號 — 裸的 true 會被當作布林值
resources:
limits:
cpu: "500m"
memory: 256Mi
value: "true" 的引號很重要。省略引號的話 Kubernetes 會以布林值接收,容器收到的是 True(Python 風格)或直接啟動失敗,取決於語言。
GitHub Actions workflow
GitHub Actions 的 workflow 是深度巢狀的 Mapping,run 區塊常見多行 shell 指令。|(literal)與 >(folded)的差異在這裡會派上用場。
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20" # 加引號以保留末尾 0
- name: Install
run: npm ci
- name: Test
run: |
npm run lint
npm run build
npm test -- --run
這裡最常見的破壞是去掉 node-version: "20" 的引號。YAML 會把它強制轉為整數 20,視 setup-node 的版本可能不被接受。凡「看起來像數字但必須是字串」的值都要加引號。
Docker Compose 服務定義
Compose 檔案混合 Mapping、Sequence、環境變數字典、bind mount 字串,短到可以完整貼出,是練習辨認縮排問題的好對象。
services:
web:
image: nginx:1.27-alpine
ports:
- "8080:80" # 加引號避免被解讀為 60 進位
environment:
NGINX_HOST: example.com
NGINX_PORT: "80" # 引號必要:環境變數值必須是字串
volumes:
- ./html:/usr/share/nginx/html:ro
depends_on:
- api
api:
build: ./api
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 5s
retries: 3
這裡的陷阱有兩個:"8080:80" 的引號去掉後 YAML 1.1 解析器可能把它當作 60 進位解析;environment: 下方的值最終都是以字串傳遞,所以加引號比較安全。
常見錯誤與各解析器的錯誤訊息(實測)
多數開發者都踩過的 YAML 陷阱:
- 用 Tab 字元縮排 — 看不見的 Tab 混入時很難找原因
- 冒號後沒有空格 —
key:value被當成單一的字串 - 縮排寬度不一致 — 同層項目必須同一深度
- 未加引號的
NO、OFF、YES、ON被轉為布林值(Norway problem) - 未加引號的值中包含冒號 —
time: 10:30可能被解讀為 60 進位 - 區塊格式與流程格式混用 — 解析器可能無法預期
- 同一 Mapping 中使用重複鍵 — 行為取決於實作
搜尋這些錯誤時最頭痛的地方:同一個錯誤在不同解析器中回報完全不同的訊息。我們把 6 個代表性錯誤分別餵給 js-yaml、yaml (eemeli)、PyYAML,記錄了實際輸出的錯誤字串(2026-07-12 實測,含 ruamel.yaml 在內的 4 種解析器原始日誌與重現腳本在儲存庫 scripts/benchmarks/yaml-syntax-errors/)。
| 錯誤內容 | js-yaml 錯誤訊息 | yaml (eemeli) 錯誤訊息 | PyYAML 錯誤訊息 |
|---|---|---|---|
| Tab 字元縮排 | tab characters must not be used in indentation(第 2 行) | Tabs are not allowed as indentation(第 2 行) | found character '\t' that cannot start any token(第 2 行) |
| 縮排不一致(2 格與 3 格混用) | bad indentation of a mapping entry(第 3 行) | Nested mappings are not allowed in compact mappings(第 2 行) | mapping values are not allowed here(第 3 行) |
值內未加引號的 : (url: http://x.com: 8080) | bad indentation of a mapping entry(第 1 行 24 欄) | Nested mappings are not allowed in compact mappings(第 1 行 6 欄) | mapping values are not allowed here(第 1 行 24 欄) |
| 雙引號未關閉 | unexpected end of the stream within a double quoted scalar(最後一行) | Missing closing "quote(第 2 行 8 欄) | while scanning a quoted scalar ... found unexpected end of stream(指向開頭引號位置) |
*alias 指向未定義的錨點 | unidentified alias "missing" | Unresolved alias (the anchor must be set before the alias): missing(無行號) | found undefined alias 'missing' |
流程 [ 未關閉 | missed comma between flow collection entries(下一行) | Flow sequence in block collection must be sufficiently indented and end with a ](第 2 行) | while parsing a flow sequence ... expected ',' or ']' |
解讀這些訊息有三個技巧。第一,PyYAML 的 mapping values are not allowed here 與 js-yaml 的 bad indentation of a mapping entry 看似不同的錯誤,實際上都是「在 Mapping 不該出現的位置有 : 」,eemeli 的 Nested mappings are not allowed in compact mappings 是同一診斷的第三種說法。第二,各解析器指出的行號不同:引號未關閉時 js-yaml 指向串流結尾,PyYAML 指向開頭引號位置,所以要同時檢查錯誤行的前後 1 行與引號起始位置。第三,eemeli 有時比 js-yaml 早 1 行指出位置,因為它回報的是 compact mapping 開始的位置而非壞掉的位置。
要最快找到這些錯誤的方法就是讓解析器去讀。不安裝任何東西就能做的話,把 YAML 貼到 FormatArc 的 YAML to JSON 轉換器 即可。有效的 YAML 會立刻顯示 JSON,無效的話會顯示語法錯誤與應跳轉的行號。
解決 "It is forbidden to specify block composed value at the same line as key"
這是 JetBrains/IntelliJ 系 IDE(IntelliJ IDEA、PyCharm、GoLand、Rider、DataGrip)的 YAML inspection 輸出的訊息。原文定義在 YAMLBundle.properties在新分頁中開啟 的 annotator.same.line.composed.value.message。
意思只有一個:區塊集合(block collection)從鍵的同一行開始了。inspection 的原始碼在新分頁中開啟顯示,檢查對象只有兩種形態:block sequence 的第一個項目與鍵在同一行,或 block mapping 的第一個鍵值與鍵在同一行。
形態 1:block sequence 從鍵所在行開始。
# 會觸發 inspection 的寫法
key: - item1
- item2
解法:把第一個項目移到下一行。
key:
- item1
- item2
形態 2:block mapping 從鍵所在行開始。
# 會觸發 inspection 的寫法
key: sub: value
sub2: value2
解法:在冒號後換行。
key:
sub: value
sub2: value2
兩種形態都是 YAML 規格上的錯誤,不是 IDE 的個人偏好。解析器也會拒絕,yaml 套件對形態 1 回傳 Unexpected block-seq-ind on same line with key,對形態 2 回傳 Nested mappings are not allowed in compact mappings。
與重複鍵的 inspection 是不同的錯誤。 Mapping 的鍵重複時會顯示 Key 'x' is duplicated(同 bundle 的 YAMLDuplicatedKeysInspection.duplicated.key)。如果 IDE 告訴你鍵重複了,解法是改鍵名或調整父節點,與區塊集合的位置無關。
Helm 或 Jinja 模板中出現此錯誤時: inspection 在偵測到模板語言注入元素時會提前結束檢查,所以正確撰寫的模板原本不應該被標記。如果仍然出現,屬於已知的假陽性(false positive,如 IJPL-64437在新分頁中開啟),檔案本身不需要修改。
修正後把檔案貼到 YAML to JSON 確認區塊集合是否已正常處理。所有轉換都在瀏覽器內完成,輸入的資料不會被上傳。
用 FormatArc 在瀏覽器中驗證 YAML
FormatArc 的瀏覽器端工具可以直接當作輕量 YAML linter 使用:


- YAML to JSON — 貼上 YAML 取得 JSON 輸出,有錯誤時附行號顯示
- JSON to YAML — 從 JSON 出發學習對應的 YAML 結構
- JSON Formatter — 美化整理轉換後的 JSON 資料
全部在瀏覽器中完成。內部的設定檔或包含機密值的資料都可以放心貼上驗證,不會外洩到任何伺服器。
常見問題
YAML 縮排可以用 Tab 嗎?
不行。YAML 規格只允許空格,Tab 字元在多數解析器中會觸發語法錯誤。編輯 .yml 或 .yaml 檔案時把編輯器設成按下 Tab 鍵輸入 2 格空格。
.yml 與 .yaml 有差別嗎?
沒有。兩種副檔名在所有 YAML 解析器中的處理完全相同。官方規格建議使用 .yaml,但 .yml 也很普遍且完全支援。
字串一定要加引號嗎?
不必。YAML 中絕大多數字串可以不加引號直接寫。需要引號的情況是:值會被誤判為其他型別,或以 -、:、[、{、|、> 等特殊字元開頭。
version: 1.0 寫了怎麼變成 1?
YAML 把 1.0 解析為浮點數,部分序列化工具再把它輸出為整數 1。要保留為字串就加引號:version: "1.0"。
縮排空格用幾格合適?
2 格是慣例。Kubernetes、Docker Compose、GitHub Actions 等大多數官方範例都用 2 格。4 格也合法,但同一個檔案內必須保持一致。
不安裝任何工具怎麼驗證 YAML?
把檔案貼到 FormatArc 的 YAML to JSON 轉換器 就能即時驗證。有效的 YAML 會立刻產出 JSON 輸出,無效的話會顯示附行號的錯誤訊息。
一個檔案可以放多份 YAML 文件嗎?
可以。用只含 --- 的獨立行作為分隔。這是 Kubernetes 把多份 manifest 包在同一個檔案中的標準做法。
總結
- YAML 以縮排表達結構,不用括號
- 縮排只用空格,保持一致
- 字串只在會被誤判時才加引號
- 註解、多行字串、錨點讓設定檔可讀性與重用性大幅提升
- 語法驗證最快的方式是把 YAML 貼到 FormatArc 的 YAML to JSON 工具