YAML front matter nested keys: three pandoc readers tested
Bobby Huang ·
The section Sample header and printing template sets out the test header next to its template. Under Three readers on the nested file, the commands sit beside the lines pandoc printed. Under A child key moved to column zero, a header with team pulled out of owner is run and its output quoted. The section A child key indented too far quotes the error pandoc wrote for an over-indented key. The section One tab before the name key shows the tab file, its run and the control count. Under Steps to check a nested header, the checks are laid out as numbered steps.
Key takeaways
- Input lines plus template syntax appear in Sample header and printing template.
- Output for markdown, commonmark_x plus commonmark+yaml_metadata_block is reported in Three readers on the nested file.
- What happened when team moved out of owner is shown in A child key moved to column zero.
- The error lines from the over-indented file sit in A child key indented too far.
- Look at Steps to check a nested header for commands you can point at a file of yours.
On this page, yaml front matter nested keys are keys typed under a parent key in the YAML header of a Markdown file, set in below that parent. I built one header with two parent keys, owner and review, and asked pandoc 3.8.3 to print the values stored under them. Three reader settings took part: markdown, commonmark_x, plus commonmark+yaml_metadata_block.
After that, I changed the indent three ways and ran the same readers again. Each command got a single pass on macOS. Results quoted here come word for word from the test log, line breaks included.
Sample header and printing template
nested.md is the starting file. The log counts it at 13 lines:
---
title: Doe plan
owner:
name: Jane Doe
team: Doe Holdings
review:
by: John Doe
steps:
- read
- sign
---
Body line.
Two keys sit under owner and two sit under review. Under review, steps carries a two-item list.
A small template file, nested-tpl.html, took the place of pandoc's default page so the stored values would print:
name=[$owner.name$]
team=[$owner.team$]
by=[$review.by$]
steps=[$for(review.steps)$$review.steps$;$endfor$]
In this template, a dotted path like $owner.name$ reaches into a parent key for its value. The $for(review.steps)$ and $endfor$ pair repeats the text between them once per list item. The ; after $review.steps$ is plain template text that marks where one item stops. Each bracket pair is plain text too. They show where a printed value starts and ends, so a missing value shows up as [].
Three readers on the nested file
From the test folder, one command per reader:
pandoc -f markdown -s --template nested-tpl.html -t html nested.md
pandoc -f commonmark+yaml_metadata_block -s --template nested-tpl.html -t html nested.md
pandoc -f commonmark_x -s --template nested-tpl.html -t html nested.md
Pandoc's markdown reader left four value lines in the log, and the script recorded the exit code after them:
name=[Jane Doe]
team=[Doe Holdings]
by=[John Doe]
steps=[read;sign;]
rc=0
Each commonmark-based run gave back those identical five lines. Both child keys under owner came through, review.by came through, and the loop wrote read and then sign, the order they have in the file.
Why does the second reader carry +yaml_metadata_block? Pandoc's own extension lists answer that. For markdown, +yaml_metadata_block turns up on line 73 of that list, and commonmark_x carries the same entry on line 30. Plain commonmark lists -yaml_metadata_block at line 30, so this test turned the extension on by name for that reader. The companion post on multiline strings in YAML front matter includes a plain commonmark run where the extension stayed off.
A child key moved to column zero
The second file, nested-outdent.md, is 8 lines long by the log's count. It keeps name: two spaces in under owner:, while team: starts at column 0:
---
title: Doe plan
owner:
name: Jane Doe
team: Doe Holdings
---
Body line.
The matching template, nested-outdent-tpl.html, asks for the team value in two places, once inside owner and once at the top level:
name=[$owner.name$]
owner.team=[$owner.team$]
team=[$team$]
The command for the markdown reader was:
pandoc -f markdown -s --template nested-outdent-tpl.html -t html nested-outdent.md
Pandoc printed this, and the script added the rc line:
name=[Jane Doe]
owner.team=[]
team=[Doe Holdings]
rc=0
Both commonmark-based runs produced those four lines too. Nothing in the log flags a problem: the exit code is 0 and no warning text appears. Even so, owner.team came back empty, and Doe Holdings turned up under a top-level key called team. Here, that header ran clean with rc 0 even though team no longer sat under owner.
A child key indented too far
nested-overindent.md also runs to 8 lines in the log. This time team: sits four spaces in, two more than name: on the line above:
---
title: Doe plan
owner:
name: Jane Doe
team: Doe Holdings
---
Body line.
Pandoc read it against the main template, nested-tpl.html:
pandoc -f markdown -s --template nested-tpl.html -t html nested-overindent.md
No values printed this time. Pandoc wrote a three-line error instead. Line one of that error holds the input's full path on the test machine; this page skips it. The other two lines and the logged exit code read:
YAML parse exception at line 4, column 8:
mapping values are not allowed in this context
rc=64
The two commonmark-based runs repeat those two lines, with rc 64 again. Set this beside the column-zero file: that header gave rc 0 with one empty value, and this one stopped the whole run before any template line came out.
One tab before the name key
nested-tab.md is 7 lines by the log's count. It has a title line and an owner: line, and then name: Jane Doe pushed in by one tab character where the other files use two spaces. A tab byte can't appear on this page, so the listing below writes it as the placeholder <TAB>:
---
title: Doe plan
owner:
<TAB>name: Jane Doe
---
Body line.
The test script checked the tab with a control step. It counted the lines holding a tab character in two files:
grep -c "$(printf '\t')" nested-tab.md
grep -c "$(printf '\t')" nested.md
Those commands printed 1 for nested-tab.md and 0 for nested.md. One line of nested-tab.md carries a tab, then, while nested.md carries zero.
The tab file ran with nested-tpl.html:
pandoc -f markdown -s --template nested-tpl.html -t html nested-tab.md
The log holds:
name=[Jane Doe]
team=[]
by=[]
steps=[]
rc=0
Under the other two readers, the log shows those five lines again. Pandoc gave Jane Doe for $owner.name$ from the tab-indented line in this test, with rc 0. The team, by and steps slots stayed empty because this short file never sets those keys. Guidance on tabs and on lining up list items lives in YAML front matter errors.
Steps to check a nested header
You can run the same kind of check on a header of your own. Swap your file name in for nested.md and put your own key paths in the template.
- Write a template with one line per value you expect. Use dotted names such as
$owner.team$and wrap each one in brackets. - Run the
pandoccommand for the reader you use, with-s --templatepointing at your template file. - Read every bracket pair. In the column-zero run, an empty
[]besideowner.teamwas the sign thatteamhad moved out ofowner. - Check the rc. The over-indented file in this test stopped with rc 64 and printed no values.
- To find out whether a file has tab characters in it, run the
grep -cline from the tab section on that file.
What an owner field and its sibling keys are meant to hold is covered in this rundown of front matter fields.
Where Sashmark fits
Two ideas in the design of Sashmark, a desktop app not yet finished, matter for any file you touch. Under the Round-Trip idea, a fix typed into the formatted page would reach the disk when you save, with every other byte left alone. Under the Changes View idea, whatever an agent altered between your visits would carry a mark, in a git repo or in a plain folder.
It is in development and has not been released. The app itself has no AI features. See the planned features, the help pages and the roadmap for where each part stands.
More at sashmark.com.
Questions
- Why is my nested value empty?
- Compare your header with the run in A child key moved to column zero.
- Why did pandoc stop with a YAML parse exception?
- The run that stopped is quoted in A child key indented too far.
- Can a tab indent a nested key?
- See One tab before the name key for what pandoc did in the test.
- How do I print a nested value from the header?
- Dotted template lines appear under Sample header and printing template.