This is the precise answer to every 'why did this value become a boolean/number/string?' question.
YAML separates syntax from data. Parsing produces a representation graph of nodes (scalar, sequence, mapping), each with a tag. A plain scalar initially has the non-specific tag ?; quoted and block scalars have !. Tag resolution then assigns real tags according to a schema:
- Failsafe schema: only
!!map,!!seqand!!str. Every scalar is a string. Safe and predictable; some tools and config loaders can be set to it. - JSON schema: plain scalars must match JSON forms exactly:
true,false,null, JSON numbers. Anything else is an error rather than a string. - Core schema (the YAML 1.2 recommendation): the JSON schema plus friendlier forms:
True/TRUE,Null,~, empty values,0ooctal,0xhex,.inf,.nan. Everything unmatched is a string.
Only !-tagged scalars (quoted or block style) are always strings; that's the formal reason quoting works.
YAML 1.1 had no schemas in this sense, only a list of "language-independent types" with regexes that included yes/no/on/off, sexagesimals, timestamps and << merge. Most parsers' "default" behaviour is one of these schemas, possibly with extensions, which is why the same file loads differently across languages.
Many libraries let you choose: ruamel.yaml typ="safe" with version (1, 1) or (1, 2), the npm yaml package's schema: 'core' | 'json' | 'failsafe' | 'yaml-1.1', Go's yaml.v3 decoding into string fields.
Example
from ruamel.yaml import YAML
doc = "a: yes\nb: 0o17\nc: 017\nd: ~\ne: '42'\nf: 1:30\n"
y12 = YAML(typ="safe") # YAML 1.2 core schema
print(y12.load(doc))
# {'a': 'yes', 'b': 15, 'c': 17, 'd': None, 'e': '42', 'f': '1:30'}
y11 = YAML(typ="safe")
y11.version = (1, 1) # YAML 1.1 resolution rules
print(y11.load(doc))
# {'a': True, 'b': '0o17', 'c': 15, 'd': None, 'e': '42', 'f': 90}
# JavaScript, npm "yaml" package:
# YAML.parse("a: yes\nb: 1", { schema: "failsafe" }) -> { a: 'yes', b: '1' }Remember
- Plain scalars are typed by the schema; quoted and block scalars are strings
- Failsafe: all strings; JSON: strict JSON forms; core: JSON plus friendly forms
- YAML 1.1 rules differ, which is why parsers disagree
Watch out for
- Assuming two parsers agree on a file's types; check which schema each one applies.