| | |

Serialization 05 ๐Ÿ“„ Choosing a Serialization Format โ€” A Decision Framework

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

The choice between YAML, TOML, JSON, and XML is not a matter of taste. Each format was designed for a specific job, and selecting the wrong one creates a gap that teams fill with external documentation, custom tooling, and workarounds for limitations the format was never meant to overcome. The decision framework in this chapter is organized around three questions: who edits the file (humans or machines), what the file represents (configuration or data exchange), and what ecosystem already consumes it.

This chapter covers the strengths and constraints of each format, the security considerations that differentiate them, and the decision matrix that maps project types to format choices. The sections that follow walk through the four formats and their native capabilities, the security and parsing risks, and the selection strategy that prioritizes human context for hand-edited files and machine efficiency for API payloads.

Key point: Use YAML for CI/CD and Kubernetes because the ecosystem requires it, TOML for application configuration because it balances explicit typing with comment support, JSON for API responses because it is universal and safe, and XML only when a schema-driven enterprise system already uses it.


Why a decision framework exists

The “best format” myth. There is no universal winner. A 2026 comparison concludes directly: “There’s no universal winner. Each format excels in different scenarios” . YAML wins for DevOps and CI/CD because those ecosystems standardized on it; TOML is best for application configuration; JSON is the safe choice for data exchange. The decision is contextual, not absolute.

The human-editing problem. A file that a person edits needs comments to explain why a value is set. JSON has no native comments; the OpenJDK project filed a ticket to extend its bot configuration format specifically because “JSON is pretty nice to work with, but has a major drawback that makes it unsuitable for config files, it doesn’t support comments” . TOML and YAML both support comments natively, which is why they dominate configuration use cases.

The security problem. YAML’s flexibility is a liability at the boundary. The yaml.load() function in Python can execute arbitrary code through tags like !!python/object/apply:os.system, and the recommendation is to always use yaml.safe_load() . JSON has no such risk: it cannot express code execution, includes, or references. TOML is safe by design because its expressive range is too narrow to embed executable constructs .


a. YAML: the DevOps standard, with caveats

YAML’s position is ecosystem-driven. GitHub Actions, GitLab CI, CircleCI, Docker Compose, Kubernetes, and Ansible all use YAML as their configuration language . A team writing CI/CD pipelines does not choose YAML because it is the best format; they choose it because the tools require it.

The format’s strengths are native comments, human readability for nested structures, and anchor/alias support for reusing configuration blocks . Its weaknesses are equally well-documented: indentation sensitivity makes whitespace errors common, implicit typing produces the “Norway problem” where unquoted NO parses as false in YAML 1.1, and the full specification is complex enough that different parsers implement different subsets .

YAML is the correct choice when the ecosystem mandates it (CI/CD, Kubernetes, Docker) and when the configuration is deeply nested and human-readable comments are essential. It is the wrong choice for simple flat configuration (TOML is simpler) or for API payloads (JSON is faster and safer).


b. TOML: the application configuration format

TOML occupies the space YAML leaves open: configuration that humans edit, with explicit typing and no implicit resolution. The format was created as a reaction to YAML’s complexity, and its design philosophy is “minimal, obvious” . A string is always quoted, a boolean is always true or false, and there is no resolver guessing whether NO means the country or a boolean.

The format’s adoption is concentrated in package management and application settings. Rust’s Cargo.toml and Python’s pyproject.toml are the two most visible use cases, and they demonstrate TOML’s strengths: table headers provide clear structure, dotted keys handle nesting without indentation sensitivity, and the type system is small enough to be predictable . A 2016 Python packaging discussion summarized the trade-off: TOML was chosen over YAML because YAML “ended up over-engineered” and JSON “sucks as a human-editable format, because it’s too strict over things like commas and has no comments” .

TOML is the correct choice for application configuration files, especially in Rust and Python projects, and for any configuration where explicit typing matters and the file is small enough that nested arrays of tables do not become unwieldy. It is the wrong choice for deeply nested data where the header syntax becomes repetitive, and for data interchange, because TOML requires a hash table at the root and cannot represent top-level arrays .


c. JSON and XML: data exchange versus schema-driven systems

JSON is the universal data exchange format. It is supported by every language’s standard library, every HTTP client, and every API framework. Its strict syntax (double-quoted keys, no trailing commas, no comments) makes parsers simple and fast, and its lack of executable constructs makes it safe at the boundary . The trade-off is that the strictness that makes JSON good for machines makes it poor for humans: no comments, no trailing commas, and quoted keys add noise to hand-edited files.

XML occupies a different niche. It is verbose, but it is expressive: attributes, namespaces, mixed content, and schema validation are native. The format remains dominant in enterprise systems, document formats (Office Open XML, SVG), and protocol definitions where schema enforcement matters . For configuration, XML has been largely overtaken by YAML and TOML “due to their content-forward syntax” . XML is the correct choice when a schema already exists (enterprise integration, document formats) or when the data model genuinely requires attributes distinct from child elements. It is the wrong choice for new application configuration.


Complete Example Session

# ============================================
# PART 1: THE SAME CONFIGURATION IN FOUR FORMATS
# ============================================

# YAML: readable, comments, indentation-sensitive
# server:
#   host: localhost
#   port: 8080  # dev port
#   debug: true

# TOML: explicit types, table headers, no indentation
# [server]
# host = "localhost"
# port = 8080  # dev port
# debug = true

# JSON: strict, no comments, machine-first
# {
#   "server": {
#     "host": "localhost",
#     "port": 8080,
#     "debug": true
#   }
# }

# XML: verbose, attributes and elements, schema-ready
# <config>
#   <server host="localhost" port="8080" debug="true"/>
# </config>

# ============================================
# PART 2: DECISION BY FILE PURPOSE
# ============================================
# CI/CD pipeline        โ†’ YAML (GitHub Actions requires it)
# Kubernetes manifest   โ†’ YAML (ecosystem standard)
# App config (Rust)     โ†’ TOML (Cargo.toml)
# App config (Python)   โ†’ TOML (pyproject.toml)
# App config (generic)  โ†’ TOML (explicit, comments)
# REST API response     โ†’ JSON (universal, safe)
# Enterprise schema     โ†’ XML (existing schema)

# ============================================
# PART 3: THE SECURITY DIMENSION
# ============================================
# YAML: yaml.load() can execute code
#       โ†’ always yaml.safe_load()
# JSON: no execution risk (no includes, no references)
# TOML: no execution risk (too narrow to embed code)
# XML: external entity attacks possible (XXE)
#       โ†’ disable external entity processing

# ============================================
# PART 4: HYBRID STACKS ARE NORMAL
# ============================================
# A single project often uses:
#   TOML for pyproject.toml / Cargo.toml
#   YAML for CI/CD workflows
#   JSON for API schemas and package.json
#   XML for legacy integration if required
#
# The choice is per-file, not per-project.

The parts covered the same data in four formats, the decision by file purpose, the security dimension per format, and the hybrid stack that most projects actually use.


Quick Reference

Format Comparison

FormatCommentsTypingIndentationBest For
YAMLYesImplicitSignificantCI/CD, K8s
TOMLYesExplicitNot significantApp config
JSONNoStrictNot significantAPIs
XMLYesSchema-drivenNot significantEnterprise

Decision Matrix

RequirementFormatReason
CI/CD pipelineYAMLEcosystem standard
Kubernetes manifestYAMLEcosystem standard
Rust package configTOMLCargo.toml
Python project configTOMLpyproject.toml
Generic app configTOMLExplicit + comments
API responseJSONUniversal, safe
Enterprise integrationXMLSchema exists

Security Comparison

FormatRiskMitigation
YAMLCode execution via yaml.loadyaml.safe_load
JSONNone (no executable constructs)โ€”
TOMLNone (too narrow)โ€”
XMLXXE (external entities)Disable external entities

Best Practices

โœ… Do This:

# Use YAML when the ecosystem requires it
# GitHub Actions, K8s, Docker Compose                           # โœ…

# Use TOML for application config
# [server]
# port = 8080                                                   # โœ…

# Use JSON for API payloads
# {"status": "ok"}                                              # โœ…

# Use safe_load for YAML
yaml.safe_load(file)                                            # โœ…

โŒ Don’t Do This:

# Force YAML into a TOML-shaped flat config
# (TOML is simpler for flat key-value)                         # โŒ

# Use JSON for hand-edited config
# (no comments, trailing comma errors)                          # โŒ 

# Use XML for new application config
# (verbose, no advantage over TOML/YAML)                        # โŒ

# Use yaml.load on untrusted input
yaml.load(file)                                                 # โŒ

Common Pitfalls

PitfallWhy It HappensFix
Choosing YAML for flat configFamiliarity, not fitTOML is simpler and typed
JSON config loses intentNo commentsUse TOML or YAML
YAML NO parsed as booleanImplicit typingQuote ambiguous values or use TOML
XML schema complexity for simple configOver-engineeringUse TOML or YAML
Security audit flags yaml.loadCode execution riskReplace with safe_load

Real-World Examples

1. CI/CD workflow (YAML)

on: push
jobs:
  test:
    runs-on: ubuntu-latest

2. Rust package (TOML)

[package]
name = "my-crate"
version = "0.1.0"

3. Python project (TOML)

[project]
name = "my-package"
dependencies = ["requests"]

4. API response (JSON)

{"status": "ok", "data": {"id": 1}}

5. Kubernetes manifest (YAML)

apiVersion: apps/v1
kind: Deployment

6. Docker Compose (YAML)

services:
  web:
    image: nginx

7. Generic app config (TOML)

[server]
port = 8080
debug = true

8. Safe YAML load

config = yaml.safe_load(open("config.yaml"))

9. JSON schema validation

{"$schema": "...", "type": "object"}

10. XML with schema

<config xmlns="...">
  <server port="8080"/>
</config>

Visual

The Decision Tree

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  WHAT IS THE FILE FOR?                                  โ”‚
โ”‚                                                         โ”‚
โ”‚  Machine-to-machine data exchange?                      โ”‚
โ”‚         โ”‚                                               โ”‚
โ”‚    โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”                                          โ”‚
โ”‚    โ–ผ         โ–ผ                                          โ”‚
โ”‚   YES        NO                                         โ”‚
โ”‚    โ”‚         โ”‚                                          โ”‚
โ”‚    โ–ผ         โ–ผ                                          โ”‚
โ”‚  JSON     Is a schema already required?                 โ”‚
โ”‚  (safe,       โ”‚                                         โ”‚
โ”‚   universal)  โ”‚                                         โ”‚
โ”‚          โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”                                    โ”‚
โ”‚          โ–ผ         โ–ผ                                    โ”‚
โ”‚         YES        NO                                   โ”‚
โ”‚          โ”‚         โ”‚                                    โ”‚
โ”‚          โ–ผ         โ–ผ                                    โ”‚
โ”‚        XML     Is the ecosystem CI/CD?                  โ”‚
โ”‚        (schema)     โ”‚                                   โ”‚
โ”‚                โ”Œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”                              โ”‚
โ”‚                โ–ผ         โ–ผ                              โ”‚
โ”‚               YES        NO                             โ”‚
โ”‚                โ”‚         โ”‚                              โ”‚
โ”‚                โ–ผ         โ–ผ                              โ”‚
โ”‚              YAML      TOML                             โ”‚
โ”‚              (standard) (explicit,                      โ”‚
โ”‚                         comments)                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Format Strengths

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  STRENGTH MATRIX                                        โ”‚
โ”‚                                                         โ”‚
โ”‚  YAML:  comments โœ“  readability โœ“  typing โœ—  safety โœ—   โ”‚
โ”‚  TOML:  comments โœ“  readability โœ“  typing โœ“  safety โœ“   โ”‚
โ”‚  JSON:  comments โœ—  readability โœ—  typing โœ“  safety โœ“   โ”‚
โ”‚  XML:   comments โœ“  readability โœ—  typing โœ“  safety โš    โ”‚
โ”‚                                                         โ”‚
โ”‚  YAML wins on nesting, loses on typing and safety       โ”‚
โ”‚  TOML wins on config, loses on ecosystem lock-in        โ”‚
โ”‚  JSON wins on exchange, loses on human editing          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The Hybrid Stack

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  A TYPICAL 2026 PROJECT                                 โ”‚
โ”‚                                                         โ”‚
โ”‚  pyproject.toml     โ—„โ”€โ”€ TOML (project metadata)         โ”‚
โ”‚  .github/workflows/ โ—„โ”€โ”€ YAML (CI/CD)                    โ”‚
โ”‚  package.json       โ—„โ”€โ”€ JSON (JS dependencies)          โ”‚
โ”‚  config.toml        โ—„โ”€โ”€ TOML (app settings)             โ”‚
โ”‚  api-response.json  โ—„โ”€โ”€ JSON (data exchange)            โ”‚
โ”‚                                                         โ”‚
โ”‚  No single format wins. Each file uses the format       โ”‚
โ”‚  its consumer requires.                                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Security Boundary

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  TRUSTED VS UNTRUSTED                                   โ”‚
โ”‚                                                         โ”‚
โ”‚  Internal config (trusted):                             โ”‚
โ”‚  YAML or TOML, comments matter, human edits             โ”‚
โ”‚                                                         โ”‚
โ”‚  External input (untrusted):                            โ”‚
โ”‚  JSON only, no execution risk                           โ”‚
โ”‚  YAML requires safe_load, no exceptions                 โ”‚
โ”‚  XML requires entity disabling                          โ”‚
โ”‚                                                         โ”‚
โ”‚  The boundary determines the format, not preference     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

FormatCommentsTypingSafetyPrimary Use
YAMLNativeImplicitRequires safe_loadCI/CD, K8s
TOMLNativeExplicitSafeApp config
JSONNoneStrictSafeAPIs
XMLNativeSchemaXXE riskEnterprise

Key takeaways:

  • The ecosystem decides more than the specification. YAML is the standard for CI/CD and Kubernetes not because it is objectively best, but because those tools standardized on it. Fighting the ecosystem costs more than any format advantage .
  • TOML is the best generic application configuration format. It has comments, explicit typing, and a syntax simple enough to avoid YAML’s implicit-typing traps. Its adoption by Cargo and pyproject.toml confirms the fit .
  • JSON is for data exchange, not human editing. Its lack of comments and strict comma rules make it hostile to hand-maintained configuration, but its universality and safety make it the default for APIs .
  • Security is format-dependent. YAML can execute code through yaml.load; JSON and TOML cannot. Any YAML from an untrusted source must use safe_load .
  • A project uses multiple formats, not one. TOML for package metadata, YAML for CI, JSON for APIs, and XML only where a schema already exists. The choice is per-file, determined by the consumer .

Remember: The question is not “which format is best” but “who edits this file and who reads it.” If a human edits it and a build tool reads it, TOML or YAML. If a machine produces it and a machine consumes it, JSON. If the ecosystem already requires a formatโ€”GitHub Actions, Kubernetes, Cargo, pyproject.tomlโ€”use that format and stop evaluating alternatives. The cost of choosing against the ecosystem is paid in custom tooling, missing editor support, and explanations to every new contributor. Choose the format the file’s consumers expect, and reserve the debate for the few files where no consumer dictates the answer.

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!