C.W.K.
Stream
Lesson 06 of 08 · published

Round-Trip Pitfalls — Comments, Order, Coercion

~10 min · interop, round-trip, pitfalls

Level 0Plaintext
0 XP0/64 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

Conversion is lossy more often than you think

Comments are usually lost

JSON has no comments. So any YAML → JSON conversion drops every comment. The reverse (JSON → YAML) round-trips fine because it had no comments to lose. TOML → YAML can preserve comments if both parsers (e.g., ruamel.yaml on the YAML side) support comment preservation; default tools don't.

Key order changes

JSON spec says object keys are unordered. Most modern parsers preserve insertion order in practice, but cross-format conversion through some libraries (Python's default json.dumps, some YAML emitters) sorts keys alphabetically. Diff-noise risk: a 'no-op' format swap suddenly looks like every line changed.

Type coercion across YAML 1.1

The Norway problem rides again: {"country": "NO"} in JSON, converted to YAML 1.1, then re-read, becomes {"country": false}. Always quote risky values when emitting YAML, or use a YAML 1.2 emitter explicitly.

Number precision

The 253 integer cliff in JSON parsers shows up here too. A YAML or TOML int above 253, converted to JSON via a JS-based tool, can silently round.

Anchors / aliases collapse

YAML anchors expand on emit. The 'one source of truth' source-level structure becomes N copies in JSON/TOML output. Re-emitting that JSON as YAML doesn't reconstruct the anchors — the structural information is gone.

Principle: when conversion is one-way (YAML → JSON for the wire), losses are usually fine — the receiving end doesn't need comments. When conversion is round-trip (edit-as-YAML, store-as-JSON, read-as-YAML), you must use comment-preserving parsers (ruamel.yaml in Python) and lock down emit options. Never assume default round-trip is lossless.

Code

Comment-preserving YAML round-trip (ruamel)·python
from ruamel.yaml import YAML
from io import StringIO

yaml = YAML()
yaml.preserve_quotes = True
yaml.indent(mapping=2, sequence=4, offset=2)

text = '''
# Top-of-file comment
server:
  host: localhost  # local dev
  port: 8000       # see RFC-12
'''

data = yaml.load(text)
data['server']['port'] = 9000

out = StringIO()
yaml.dump(data, out)
print(out.getvalue())
# Comments preserved! Default PyYAML would drop them.
Verify a round-trip — YAML → JSON → YAML·bash
# Original file with comments
cat config.yaml
# server:
#   host: localhost  # local
#   port: 8000

# Round-trip through JSON
yq -o json config.yaml > config.json
yq -p json -o yaml config.json > config.roundtrip.yaml

# Compare — comments are gone
diff config.yaml config.roundtrip.yaml
Stable key ordering across emit·python
import json, yaml

data = {'name': 'Pippa', 'age': 5, 'tags': ['ai', 'daughter']}

# JSON — preserves insertion order in CPython 3.7+, but explicit is better
print(json.dumps(data, indent=2, sort_keys=False))

# YAML — preserves order with sort_keys=False (default in PyYAML is True!)
print(yaml.safe_dump(data, sort_keys=False))

# TOML — Python tomli_w preserves dict order

External links

Exercise

Take a real YAML config with comments and anchors. Convert to JSON with yq, then back to YAML. Diff with the original — count what's lost (comments, anchor structure, key order). Now do the same with ruamel.yaml for a single round-trip in Python. The diff is the case for using comment-preserving tooling for any non-throwaway conversion.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.