YAML Lists and Mappings Without Confusion in Real Files
Block and flow styles for sequences and mappings, lists of mappings, empty collections and why a missing value is None, each with its parsed result.

Everything in a YAML document is a scalar (a string, number, boolean or null), a sequence (a list) or a mapping (a dictionary), nested as deeply as you like. The confusing part is not the three kinds. It is that each of the collections can be written in two styles, that lists of mappings have an indentation rule of their own and that an empty value is not an empty collection. This post lays out each form with what it parses to, using a real parser.
Mappings
A mapping is a set of key and value pairs. In block style each pair is on its own line, with the same indentation, and the colon must be followed by a space:
name: web
port: 8080
tls:
enabled: true
cert: /etc/ssl/web.pemThe value of tls is itself a mapping, indented under it. In flow style the same data fits on one line:
name: web
port: 8080
tls: {enabled: true, cert: /etc/ssl/web.pem}Both load to the same dictionary. Keys are unique in a mapping, in theory. PyYAML does not enforce it:
== duplicate keys PyYAML
{'a': 2}A document with a: 1 and later a: 2 loaded with only the last value and no warning. ruamel.yaml raises a DuplicateKeyError, and yamllint reports a key-duplicates error. If a file has been merged or edited by several people, check for this (YAML duplicate keys and the silent overwrite covers it in depth).
Keys do not have to be strings. 1: one has the integer 1 as a key, and a key that looks like a boolean, such as on:, becomes True in YAML 1.1. A key that is a list or a mapping needs the explicit ? syntax and is rare. Quote keys that look like other types, as you would values.
Sequences
A sequence is an ordered list. In block style each item starts with a dash and a space:
features:
- search
- export
- billingIn flow style: features: [search, export, billing]. Both give ['search', 'export', 'billing']. Items can be any kind of node, including other lists or mappings. A list directly inside a mapping may be indented under its key, as above, or sit at the key's own indentation, with the dashes under the key. The parser accepts both (YAML indentation rules and why tabs break everything shows they load identically).
Lists of mappings
Most real configuration is a list of records. The dash introduces the item, and the item's keys are indented to line up with the first key, which starts two columns after the dash:
- name: a
v: 1
- name: b
v: 2[{'name': 'a', 'v': 1}, {'name': 'b', 'v': 2}]The v is aligned with name. If you put it under the dash instead, the structure changes or the parser errors. Another valid form puts the first key on the line after the dash, which is unusual and has no advantage:
-
name: a
v: 1Nesting continues in the same way. A list inside an item is a key whose value is indented list, and the dashes go two columns in from the key. Count columns carefully, or generate nested structures with a program.
Mixed structures
Block and flow style can be mixed freely, which is convenient and easy to abuse:
a: [1, 2, {b: 3}]
c: {d: [4, 5]}{'a': [1, 2, {'b': 3}], 'c': {'d': [4, 5]}}A document can also be JSON. YAML 1.2 was designed so that JSON is nearly a subset of it, and the parsers accept it:
== json is yaml
{'a': [1, 2, {'b': None}], 'c': 'x'}That gives you a migration path in either direction (YAML versus JSON for configuration), and a trap: a file that began as JSON and was hand-edited into something that is neither is hard to read.
Empty collections and null
An empty value is not an empty collection:
a: []
b: {}
c:{'a': [], 'b': {}, 'c': None}a: [] is an empty list and b: {} an empty mapping. c: with nothing after the colon is None. In code, for x in config["c"] fails on None and works on []. Be explicit about which you mean, and write [] or {} for an empty collection. A missing key is a third state, different again (YAML null values and the empty string question).
A related slip: a key whose list is commented out entirely.
features:
# - search
# - exportThe result is features: None, not an empty list, because there are no items. Code that expects a list breaks. Either write features: [] or have the code treat None as empty.
Anchors and merging
Repeated blocks can be shared with anchors and aliases (&name and *name), and mappings can be merged with <<. The syntax is compact and the rules are particular, so it has its own post (YAML anchors, aliases and merge keys).
Habits for readable structure
Prefer block style for anything with more than three entries or more than one level.
Use flow style for short leaf collections, such as
ports: [80, 443].Keep list items homogeneous. A list of strings with one mapping in the middle is a bug waiting.
Order keys the way people read them: the identifying key (
name,id) first.Validate the shape with a schema rather than trusting the indentation (validating config YAML with JSON Schema).
Round-trip through a formatter before committing, so that style is uniform (the YAML formatter is one).
Checking what you wrote
After editing a nested structure, load it and print the result instead of trusting your eyes. A one-line command does it: python3 -c "import yaml,sys,json; print(json.dumps(yaml.safe_load(open(sys.argv[1])), indent=1, default=str))" file.yaml shows the parsed structure as JSON, where braces and brackets make the nesting explicit. If a key you meant to nest appears at the top level, or a list appears where you expected a mapping, you see it at once. The same check in a browser is the YAML to JSON tool.
For files that matter, turn the check into a test. Load the file in a unit test and assert the paths your application reads: that services.web.ports is a list, that tls.enabled is a boolean, that no key is None unexpectedly. Such a test catches an indentation slip in a pull request, which a human reviewer can miss because the diff of a misplaced line looks like any other change.
Related reading
The cluster hub is YAML syntax you need to stop guessing at. A quick reference of all forms is in a cheat sheet of YAML syntax. Collections are specified in section 8.2 of the YAML 1.2.2 specification, and Python's handling in the PyYAML documentation.
When nested lists and mappings need a different layout
The nesting goes past four levels. Deep structures are hard to edit and review. Flatten with prefixes in keys, or split into separate files.
Order or uniqueness matters. A mapping has no guaranteed order in some parsers and a list can hold duplicates. If order matters, use a list. If uniqueness matters, validate it.
The data is tabular. A hundred rows of identical records are clearer as CSV, with YAML holding only the settings around it (everything a CSV file does and does not promise you).
The consumer expects a particular shape. Kubernetes, Compose and CI systems define their own schemas. Follow theirs, and validate with their tooling.


