| | |

YAML 09 ๐Ÿ“„ Converting and Mapping Between JSON and YAML

โฑ๏ธ Estimated read: 10 min | |

JSON and YAML are not competitors at the boundary; they are dialects of the same data model. YAML 1.2 was designed so that every valid JSON document is also valid YAML . The conversion between them is therefore a transformation of syntax, not semantics. But the syntax carries information that the other format cannot express: YAML has comments, custom tags, anchors, and formatting styles; JSON has none of these. A conversion that ignores this asymmetry loses data silently, and a conversion that tries to preserve it produces files the target format’s consumers cannot read.

This chapter covers the conversion mechanics, the tooling that performs them, and the information that is destroyed in each direction. It explains why JSON-to-YAML is a subset operation while YAML-to-JSON is a lossy projection, how yq and the Python yq wrapper handle the difference, and the pitfalls that produce valid output with wrong meaning. The sections that follow walk through the subset relationship, the tooling, and the lossy boundaries.

Key point: Every JSON document is valid YAML, so JSON-to-YAML is safe and lossless; YAML-to-JSON is lossy because comments, custom tags, anchors, and formatting have no JSON representation, and the conversion must either drop them or encode them in a side channel.


Why the conversion is asymmetric

The subset problem. YAML 1.2’s primary objective was to bring YAML into compliance with JSON as an official subset . This means a JSON parser and a YAML parser reading the same JSON text produce the same data. The reverse is not true: a YAML document with comments, anchors, or custom tags is not valid JSON, and converting it to JSON requires deciding what to do with the parts JSON cannot represent. A YAML block with a !Ref tag or a << merge key has no JSON equivalent; the converter must either resolve the construct into its expanded form or discard it.

The tag problem. YAML tags carry type information that JSON does not have. A !!timestamp scalar is a point in time in YAML; in JSON it becomes a string, and the type information is lost. A custom tag like !env carries application semantics that the JSON consumer cannot interpret. The Python yq wrapper’s -Y roundtrip option addresses this by injecting metadata pairs into the JSON representation so the tags can be restored on the way back, at the cost of making the JSON non-standard for any consumer that does not know about the metadata .

The comment problem. JSON has no comments. YAML comments are discarded in every YAML-to-JSON conversion, and no roundtrip option restores them because the JSON representation has no place to store them . A configuration file that was converted to JSON and back will be syntactically equivalent and semantically stripped of every explanation the author wrote.


a. The tooling landscape

The conversion tools fall into three categories: general-purpose data processors, single-purpose converters, and language libraries.

yq (Go) is the most capable. It auto-detects input format, converts between YAML, JSON, XML, CSV, TOML, and more, and preserves JSON-to-YAML readability with the -P flag . The Go version handles multi-document YAML and is the tool most commonly used in CI pipelines.

# JSON to YAML
yq -P -oy input.json

# YAML to JSON
yq -p yaml -o json input.yaml

yq (Python) is a different tool despite the shared name. It converts YAML to JSON, pipes it to jq for processing, and optionally converts back to YAML with -y. The -Y flag enables roundtrip mode, which preserves tags and styles by injecting metadata into the JSON . The trade-off is that the JSON is no longer plain data; a jq filter that counts array entries will count the metadata keys as well .

# YAML to JSON, process with jq, back to YAML
cat input.yml | yq -y '.foo.bar'

# Roundtrip with tag preservation
yq -Y '.Resources' input.yml

Single-purpose converters (jyml, json-converter, ymljson-cli, dkit) are smaller and simpler. jyml is zero-config: pass a file, get the converted file with the swapped extension . ymljson-cli adds JSON Schema validation on top of the conversion . dkit is a Rust binary that handles JSON, YAML, TOML, CSV, and more . These tools are appropriate when the task is a one-off conversion and the roundtrip semantics do not matter.


b. The JSON-to-YAML direction

JSON-to-YAML is the safe direction. The JSON is parsed into a data structure, and the data structure is serialized as YAML. Every JSON value has a YAML representation; the conversion is a serialization choice, not a data transformation.

The decisions are stylistic: indentation width, whether to use block or flow style for collections, whether to quote strings that look like other types. A JSON string "true" must be quoted in YAML or it becomes a boolean. A JSON number 1.0 may serialize as 1 in YAML, losing the float distinction. A JSON key that contains a colon must be quoted.

{"name": "app", "version": "1.10", "enabled": true}
# Correct: strings that look like booleans and numbers are quoted
name: app
version: "1.10"
enabled: true

The yq Go tool’s -P flag produces “idiomatic YAML” with block style and proper quoting . The Python yq wrapper’s -y flag converts jq output back to YAML . Both handle the quoting rules; a naive string concatenation does not.


c. The YAML-to-JSON direction and its losses

YAML-to-JSON is the lossy direction. The parser resolves anchors, expands merge keys, applies schema resolution to plain scalars, and discards comments. The JSON output contains the resolved data, not the original document.

The losses are concrete. A YAML file with <<: *defaults becomes a JSON object with the defaults inlined; the anchor and merge key are gone, and the JSON consumer cannot tell which keys were inherited. A YAML file with !env DATABASE_URL either fails to parse (if the custom tag is unregistered) or produces a JSON value that has lost the tag. A YAML file with # database port loses the comment entirely.

The Python yq wrapper’s -Y flag is the only mainstream tool that attempts to preserve tags and styles across the conversion. It does so by adding metadata entries to the JSON representation, which means the JSON is no longer a plain data document . This is a deliberate trade-off: the roundtrip is preserved at the cost of interoperability. A tool that consumes the intermediate JSON without knowing about the metadata will misread it.


Complete Example Session

# ============================================
# PART 1: JSON TO YAML (SAFE DIRECTION)
# ============================================
# input.json
# {
#   "name": "app",
#   "version": "1.10",
#   "features": ["auth", "billing"],
#   "database": { "host": "localhost", "port": 5432 }
# }

# Command
# yq -P -oy input.json

# Output
name: app
version: "1.10"          # quoted to preserve string
features:
  - auth
  - billing
database:
  host: localhost
  port: 5432

# ============================================
# PART 2: YAML TO JSON (LOSSY DIRECTION)
# ============================================
# input.yaml
# defaults: &defaults
#   timeout: 30
# service:
#   <<: *defaults
#   endpoint: /api
#   # the service endpoint

# Command
# yq -p yaml -o json input.yaml

# Output
# {
#   "defaults": { "timeout": 30 },
#   "service": { "timeout": 30, "endpoint": "/api" }
# }
# The anchor is resolved, the merge key is expanded,
# and the comment is gone.

# ============================================
# PART 3: ROUNDTRIP WITH TAG PRESERVATION
# ============================================
# Python yq with -Y preserves custom tags via metadata
# yq -Y '.Resources' template.yaml
# The JSON contains extra metadata keys for tags/styles
# A plain JSON consumer will see the metadata and may break

# ============================================
# PART 4: THE QUOTING TRAP
# ============================================
# JSON: {"enabled": "true"}   (string "true")
# Naive YAML: enabled: true   (boolean, wrong)
# Correct YAML: enabled: "true" (string preserved)

The parts covered the safe JSON-to-YAML direction, the lossy YAML-to-JSON direction with anchor and comment loss, the roundtrip metadata trade-off, and the quoting trap that changes types.


Quick Reference

Direction Safety

DirectionLosslessLosses
JSON โ†’ YAMLYesNothing (syntax only)
YAML โ†’ JSONNoComments, tags, anchors, style

Tool Comparison

ToolJSONโ†’YAMLYAMLโ†’JSONTag Preservation
yq (Go)-P -oy-p yaml -o jsonNo
yq (Python)-yDefault-Y
jymlAutoAutoNo
ymljson-cliYesYesNo

What JSON Cannot Represent

YAML FeatureJSON Result
CommentDiscarded
Anchor/aliasResolved
Merge keyExpanded
Custom tagError or lost
Block styleReformatted

Best Practices

โœ… Do This:

# Quote strings that look like other types
version: "1.10"                                              # โœ…

# Accept that YAML-to-JSON drops comments
# Keep the YAML as the source of truth                        # โœ…

# Use yq -P for readable JSON-to-YAML output
# yq -P -oy input.json                                       # โœ…

โŒ Don’t Do This:

# Convert YAML with comments to JSON and expect to get them back
# (comments are gone)                                         # โŒ

# Use -Y roundtrip JSON with a plain JSON consumer
# (metadata keys break the schema)                            # โŒ 

# Naive string concatenation for JSON-to-YAML
# (quoting and indentation will be wrong)                     # โŒ

Common Pitfalls

PitfallWhy It HappensFix
"true" becomes booleanUnquoted in YAMLQuote in output
Anchors and merges lostResolved during parseAccept loss or use roundtrip
Comments disappearJSON has no commentsKeep YAML as source
-Y JSON breaks consumerMetadata keys addedDo not use -Y for interchange
1.0 becomes 1Float serializationCheck number formatting

Real-World Examples

1. JSON to YAML with yq

yq -P -oy input.json

2. YAML to JSON with yq

yq -p yaml -o json input.yaml

3. Roundtrip with tags

yq -Y '.Resources' template.yaml

4. Zero-config conversion

npx jyml config.yaml

5. Schema-validated conversion

ymljson -i data.yaml --schema schema.json

6. Quoting a boolean-like string

enabled: "true"

7. Anchor resolved to JSON

# YAML <<: *defaults โ†’ JSON inlined object

8. Comment loss

# YAML comment โ†’ no JSON representation

9. Multi-document YAML to JSON

# yq outputs separate documents or an array

10. Python yq with jq

cat input.yml | yq -y '.foo.bar'

Visual

The Subset Relationship

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  YAML 1.2                                               โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”           โ”‚
โ”‚  โ”‚  JSON (valid YAML subset)                โ”‚           โ”‚
โ”‚  โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”              โ”‚           โ”‚
โ”‚  โ”‚  โ”‚ {"name": "app"}        โ”‚              โ”‚           โ”‚
โ”‚  โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜              โ”‚           โ”‚
โ”‚  โ”‚                                          โ”‚           โ”‚
โ”‚  โ”‚  Comments, anchors, tags, block style    โ”‚           โ”‚
โ”‚  โ”‚  (YAML-only features)                    โ”‚           โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜           โ”‚
โ”‚                                                         โ”‚
โ”‚  Every JSON document is valid YAML                      โ”‚
โ”‚  Not every YAML document is valid JSON                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Conversion Losses

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  YAML โ†’ JSON                                            โ”‚
โ”‚                                                         โ”‚
โ”‚  YAML                          JSON                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”         โ”‚
โ”‚  โ”‚ # comment    โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚ (gone)       โ”‚         โ”‚
โ”‚  โ”‚ &anchor      โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚ resolved     โ”‚         โ”‚
โ”‚  โ”‚ <<: *merge   โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚ expanded     โ”‚         โ”‚
โ”‚  โ”‚ !custom      โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚ error/lost   โ”‚         โ”‚
โ”‚  โ”‚ block style  โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚ reformatted  โ”‚         โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜         โ”‚
โ”‚                                                         โ”‚
โ”‚  The data is preserved; the document is not             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Tool Decision

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  WHICH TOOL?                                            โ”‚
โ”‚                                                         โ”‚
โ”‚  Need tags/styles preserved?                            โ”‚
โ”‚         โ”‚                                               โ”‚
โ”‚    โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”                                          โ”‚
โ”‚    โ–ผ         โ–ผ                                          โ”‚
โ”‚   YES        NO                                         โ”‚
โ”‚    โ”‚         โ”‚                                          โ”‚
โ”‚    โ–ผ         โ–ผ                                          โ”‚
โ”‚  yq -Y     Is it a one-off?                             โ”‚
โ”‚  (Python)       โ”‚                                       โ”‚
โ”‚            โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”                                  โ”‚
โ”‚            โ–ผ         โ–ผ                                  โ”‚
โ”‚          YES        NO                                  โ”‚
โ”‚            โ”‚         โ”‚                                  โ”‚
โ”‚            โ–ผ         โ–ผ                                  โ”‚
โ”‚         jyml     yq (Go)                                โ”‚
โ”‚         (zero-   (CI, multi-                            โ”‚
โ”‚          config)  format)                               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Roundtrip Metadata

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  yq -Y (PYTHON) ROUNDTRIP                               โ”‚
โ”‚                                                         โ”‚
โ”‚  YAML: !Ref MyResource                                  โ”‚
โ”‚         โ”‚                                               โ”‚
โ”‚         โ–ผ                                               โ”‚
โ”‚  JSON: { "Fn::Tag": "!Ref",                             โ”‚
โ”‚          "value": "MyResource" }                        โ”‚
โ”‚         โ”‚                                               โ”‚
โ”‚         โ–ผ                                               โ”‚
โ”‚  yq -Y back: !Ref MyResource                            โ”‚
โ”‚                                                         โ”‚
โ”‚  Plain JSON consumer sees the metadata keys             โ”‚
โ”‚  and may break on the unexpected structure              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Subset relationshipJSON is valid YAML 1.2
JSON โ†’ YAMLLossless, syntax-only
YAML โ†’ JSONLossy: comments, tags, anchors, style
yq (Go)JSONโ†’YAML: -P -oy
yq (Python)Roundtrip tags: -Y
Zero-configjyml
Schema validationymljson-cli

Key takeaways:

  • JSON-to-YAML is safe. Every JSON document is valid YAML, and the conversion is a serialization choice. The only risks are quoting (a string "true" must stay quoted) and number formatting.
  • YAML-to-JSON is lossy. Comments, custom tags, anchors, merge keys, and formatting have no JSON representation. The conversion produces the resolved data, not the original document .
  • yq has two implementations with different capabilities. The Go version is a general data processor; the Python version wraps jq and offers -Y for tag preservation at the cost of non-standard JSON .
  • Roundtrip metadata breaks plain consumers. A JSON document produced by yq -Y contains extra keys for tags and styles. A consumer that does not know about them will misread the data or fail validation .
  • Keep the YAML as the source of truth. If comments and formatting matter, do not convert to JSON and back. Use JSON for interchange and YAML for authoring, and accept that the two are different representations with different expressive ranges.

Remember: JSON and YAML are not two formats for the same job; they are two points on a spectrum. JSON is for machines: strict, safe, no comments. YAML is for humans: readable, expressive, with comments and tags. Converting from JSON to YAML adds readability; converting from YAML to JSON removes expression. The conversion is not reversible when the YAML used a feature JSON does not have. Choose the direction based on which side needs the file, and do not expect the roundtrip to return what it took.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!