YAML 1.2 Merge Key Precedence and Duplicate Key Handling
0 reputation · 16 Feb 2025, 14:10 UTC
0 reputation · 16 Feb 2025, 14:10 UTC
The YAML 1.2 specification introduces the merge key ! to facilitate map merging. However, the specification does not explicitly define precedence rules when a map contains multiple merge keys or when nested merges are applied.
This lack of a defined standard creates uncertainty during serialization and deserialization, particularly when attempting to maintain idempotent writes. Different parser implementations may resolve duplicate keys or merge order differently, leading to inconsistent data structures across different environments.
Which behavior is considered the standard for merge key precedence when multiple aliases are merged into a single map? How do different YAML 1.2 compliant parsers typically resolve conflicts between duplicate keys during a merge operation?
32275 reputation · 16 Feb 2025, 20:10 UTC
There is no YAML 1.2 standard for merge-key precedence. The merge key is <<, not !; it originates in YAML 1.1 and is not part of the YAML 1.2 core schema. Because the 1.2 spec does not define <<, parsers are free to implement it differently, and "YAML 1.2 compliant" does not guarantee identical merge behavior.
Despite the lack of a 1.2 standard, a widely implemented convention exists:
<<.<< fills only absent keys.<< takes a sequence, earlier entries override later ones (first occurrence wins).If a document literally contains two separate << keys in the same mapping, that is a duplicate-key situation, not a merge-precedence situation. YAML 1.2 core requires unique keys, but many parsers historically allow duplicates with last-value-wins.
Duplicate-key enforcement is version-sensitive. For example, PyYAML and SnakeYAML traditionally allow duplicates while supporting <<; js-yaml v4 rejects duplicate keys by default; Go and Ruby parsers vary by release and options. These defaults can change across major versions, so behavior observed in one release may not hold in another.
To avoid non-deterministic output, do not emit multiple << entries or duplicate keys. Expand merges into one fully resolved mapping before dumping—optionally key-sorted—then re-parse and compare the resolved structures. A mismatch means the writer is not idempotent for that input.
Because library defaults differ, confirm empirically with a minimal document:
base: &base
a: 1
b: 2
override: &override
a: 10
target:
<<: [*override, *base]
b: 20
a: 30Load the file and print the resolved mapping. Under the de-facto rule you should see a: 30 and b: 20 because explicit keys win. To test sequence precedence, remove the explicit a and confirm the earlier sequence entry (*override) wins. To test duplicate-key handling, repeat a key inside one mapping and observe whether your parser errors, warns, or last-wins. Record the exact library name and version, and check its changelog for merge-key and duplicate-key handling before relying on a default.
The safest serialization strategy depends on your parser and version. If you share the language, library, and exact version you are targeting, the recommendation can be narrowed to that environment.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.