JSON to YAML Conversion: Complete Guide for Developers

Convert JSON to YAML Instantly

Need a quick conversion? Use our JSON to YAML Converter to transform your data in one click, directly in your browser. No installation required.

1. Why Convert Between JSON and YAML

JSON and YAML are two of the most widely used data serialization formats in software development. While they can represent the same data structures, they serve different niches in the development ecosystem, and you will frequently need to convert between them.

Here are the most common scenarios that call for JSON-to-YAML conversion (or vice versa):

Understanding the relationship between these formats, and knowing how to convert between them correctly, is a practical skill that every developer working with modern infrastructure tools needs.

2. Key Differences Between JSON and YAML Syntax

JSON and YAML can represent the same data structures (objects, arrays, strings, numbers, booleans, and null), but their syntax differs significantly. Understanding these differences is essential for accurate manual or programmatic conversion.

Objects / Mappings

JSON uses curly braces and colons with quoted keys. YAML uses indentation and colons without braces or quotes:

JSON
{
  "name": "web-app",
  "version": "2.1.0",
  "private": true
}
YAML
name: web-app
version: "2.1.0"
private: true

Notice that YAML does not require quotes around most strings. However, the version string "2.1.0" is quoted in the YAML example to prevent it from being interpreted as a number. This is one of the subtleties that trips people up during conversion.

Arrays / Sequences

JSON uses square brackets. YAML uses dashes with indentation:

JSON
{
  "fruits": ["apple", "banana", "cherry"]
}
YAML
fruits:
  - apple
  - banana
  - cherry

YAML also supports an inline flow syntax that looks identical to JSON: fruits: [apple, banana, cherry]. This can be useful for short lists.

Nested Structures

JSON nests with braces and brackets. YAML nests with indentation levels:

JSON
{
  "database": {
    "host": "localhost",
    "port": 5432,
    "credentials": {
      "user": "admin",
      "password": "secret"
    }
  }
}
YAML
database:
  host: localhost
  port: 5432
  credentials:
    user: admin
    password: secret

Data Types

Both formats support strings, numbers, booleans, null, arrays, and objects. However, YAML's type inference is more aggressive:

Feature JSON YAML
Strings Always double-quoted Usually unquoted; single or double quotes optional
Numbers Integer or float Integer, float, octal (0o14), hex (0xFF)
Booleans true, false true, false, yes, no, on, off (YAML 1.1)
Null null null, ~, or empty value
Dates String only (no native date type) Native date parsing (2026-02-11)
Comments Not supported # inline and full-line comments
Multi-document Not supported Supported via --- separator

Validate Your JSON Before Converting

Malformed JSON will produce broken YAML. Always validate first with our JSON Validator to catch syntax errors before conversion.

3. YAML Features Not Available in JSON

YAML is a superset of JSON (every valid JSON document is also valid YAML), but YAML offers several features that have no JSON equivalent. When converting YAML to JSON, these features must be resolved or lost.

Comments

YAML supports comments with the # character. This is one of the most-cited reasons developers prefer YAML for configuration files:

# Database configuration
database:
  host: localhost      # Use 127.0.0.1 in Docker
  port: 5432           # Default PostgreSQL port
  max_connections: 100  # Increase for production
  # SSL settings (uncomment in production)
  # ssl: true
  # ssl_cert: /etc/ssl/db.pem

When converting this to JSON, all comments are stripped. There is no way to preserve them. This is a one-way loss: JSON has no comment syntax.

Anchors and Aliases

YAML supports anchors (&) and aliases (*) for reusing values and avoiding duplication:

defaults: &defaults
  adapter: postgres
  host: localhost
  port: 5432

development:
  database:
    <<: *defaults
    database: myapp_dev

production:
  database:
    <<: *defaults
    database: myapp_prod
    host: db.example.com

When converting to JSON, anchors and aliases are resolved into their expanded form. The resulting JSON will have duplicated values everywhere the anchor was referenced. The DRY (Don't Repeat Yourself) benefit is lost:

{
  "defaults": {
    "adapter": "postgres",
    "host": "localhost",
    "port": 5432
  },
  "development": {
    "database": {
      "adapter": "postgres",
      "host": "localhost",
      "port": 5432,
      "database": "myapp_dev"
    }
  },
  "production": {
    "database": {
      "adapter": "postgres",
      "host": "localhost",
      "port": 5432,
      "database": "myapp_prod",
      "host": "db.example.com"
    }
  }
}

Multi-line Strings

YAML provides two block scalar styles for multi-line strings that are far more readable than JSON's escaped newlines:

Literal block scalar (|): Preserves newlines exactly as written.

script: |
  #!/bin/bash
  echo "Starting deployment..."
  docker build -t myapp .
  docker push myapp:latest
  kubectl apply -f deploy.yaml

Folded block scalar (>): Folds newlines into spaces (like a paragraph), preserving blank lines as actual newlines.

description: >
  This is a long description that spans
  multiple lines but will be folded into
  a single paragraph when parsed.

  A blank line creates a real newline.

In JSON, the equivalent requires explicit \n escape sequences crammed into a single string:

{
  "script": "#!/bin/bash\necho \"Starting deployment...\"\ndocker build -t myapp .\ndocker push myapp:latest\nkubectl apply -f deploy.yaml\n"
}

Merge Keys

The merge key (<<) lets you merge one mapping into another, combined with anchors:

base_service: &base
  restart: always
  networks:
    - backend

web:
  <<: *base
  image: nginx:latest
  ports:
    - "80:80"

api:
  <<: *base
  image: node:20
  ports:
    - "3000:3000"

This is extremely useful in Docker Compose files and CI/CD configurations where services share common settings. Converting to JSON expands every merge into the full set of duplicated keys.

Multiple Documents

A single YAML file can contain multiple documents separated by ---:

---
apiVersion: v1
kind: Service
metadata:
  name: web
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web

JSON has no equivalent. Converting a multi-document YAML file to JSON requires either splitting into multiple JSON files or wrapping them in an array.

4. Common Conversion Gotchas and Edge Cases

Converting between JSON and YAML is usually straightforward, but several edge cases can introduce subtle bugs if you are not careful.

The Norway Problem (Boolean Coercion)

In YAML 1.1 (used by many older parsers, including PyYAML by default), the bare strings yes, no, on, off, y, and n are interpreted as booleans. This is the infamous "Norway problem": the country code NO for Norway becomes false.

# YAML 1.1 interpretation:
countries:
  - GB    # string "GB"
  - FR    # string "FR"
  - NO    # boolean false!
  - DE    # string "DE"

The fix is to quote the value: "NO". YAML 1.2 (the current spec) removes most of these boolean aliases, accepting only true and false. However, many tools still use YAML 1.1 parsers.

When converting JSON to YAML, any string value that matches a YAML boolean keyword must be quoted in the output. A good converter handles this automatically.

Numeric String Coercion

YAML auto-parses values that look like numbers. The string "1.0" in JSON becomes the float 1.0 in YAML if not quoted:

# This YAML:
version: 1.0

# Parses as the number 1.0, not the string "1.0"
# In JSON it becomes: {"version": 1}  (or 1.0)

Version numbers, zip codes, phone numbers, and any string that happens to look numeric must be quoted in YAML:

version: "1.0"
zip_code: "01onal"
phone: "+1-555-0123"

Date String Coercion

YAML 1.1 parsers interpret strings like 2026-02-11 as date objects rather than strings:

# This might become a date object, not a string
release_date: 2026-02-11

# Quote it to keep it as a string
release_date: "2026-02-11"

Indentation Sensitivity

YAML is indentation-sensitive, and inconsistent indentation causes parsing failures. Unlike Python, which requires consistent indentation within a block, YAML requires consistent indentation within each level but allows different amounts per level. Most teams standardize on 2-space indentation:

# Valid but confusing (mixed indent widths):
root:
  level1:
      level2: value

# Better (consistent 2-space indent):
root:
  level1:
    level2: value

When generating YAML from JSON programmatically, always specify a consistent indent width.

Special Characters in Keys

JSON keys are always quoted strings, so they can contain any characters. YAML keys are usually unquoted, which means certain characters require quoting:

# These YAML keys need quoting:
"key: with colon": value
"key with #hash": value
"{bracket key}": value
"null": actual string null

Trailing Whitespace and Newlines

YAML block scalars have chomp indicators that control trailing newlines: |+ (keep all), |- (strip all), and | (clip to single newline). A round-trip conversion through JSON may alter trailing whitespace because JSON strings have no such mechanism.

Validate Your YAML Output

After converting, always validate the result. Our YAML Validator catches indentation errors, type coercion issues, and syntax problems before they cause deployment failures.

5. Converting JSON to YAML in Different Languages

Every major programming language has mature libraries for JSON-to-YAML conversion. Below are practical examples for the four most common languages used in DevOps and backend development.

JavaScript / Node.js

The js-yaml package is the standard YAML library for JavaScript:

// Install: npm install js-yaml

const yaml = require('js-yaml');
const fs = require('fs');

// JSON string to YAML
const jsonString = `{
  "apiVersion": "apps/v1",
  "kind": "Deployment",
  "metadata": {
    "name": "web-app",
    "labels": {
      "app": "web"
    }
  },
  "spec": {
    "replicas": 3
  }
}`;

const jsonObj = JSON.parse(jsonString);
const yamlString = yaml.dump(jsonObj, {
  indent: 2,
  lineWidth: 120,
  noRefs: true,         // Don't use anchors/aliases
  sortKeys: false,      // Preserve key order
  quotingType: '"',     // Use double quotes when quoting
  forceQuotes: false    // Only quote when necessary
});

console.log(yamlString);
// Output:
// apiVersion: apps/v1
// kind: Deployment
// metadata:
//   name: web-app
//   labels:
//     app: web
// spec:
//   replicas: 3

// YAML to JSON
const yamlInput = fs.readFileSync('config.yaml', 'utf8');
const parsed = yaml.load(yamlInput);
const jsonOutput = JSON.stringify(parsed, null, 2);
console.log(jsonOutput);

// YAML to JSON with schema safety (recommended)
const safeParsed = yaml.load(yamlInput, {
  schema: yaml.JSON_SCHEMA  // Strict JSON-compatible types only
});

Python

Python has two main YAML libraries. PyYAML is the classic choice; ruamel.yaml is the modern alternative with better YAML 1.2 support:

# Install: pip install pyyaml

import json
import yaml

# JSON to YAML
json_data = '''
{
  "services": {
    "web": {
      "image": "nginx:latest",
      "ports": ["80:80", "443:443"],
      "environment": {
        "NODE_ENV": "production",
        "DEBUG": "false"
      }
    },
    "db": {
      "image": "postgres:16",
      "volumes": ["pgdata:/var/lib/postgresql/data"]
    }
  }
}
'''

data = json.loads(json_data)
yaml_output = yaml.dump(data,
    default_flow_style=False,  # Block style (not inline)
    indent=2,
    sort_keys=False,           # Preserve insertion order
    allow_unicode=True
)
print(yaml_output)

# YAML to JSON
with open('docker-compose.yaml', 'r') as f:
    yaml_data = yaml.safe_load(f)  # Always use safe_load!

json_output = json.dumps(yaml_data, indent=2)
print(json_output)

# Using ruamel.yaml for YAML 1.2 compliance
# Install: pip install ruamel.yaml
from ruamel.yaml import YAML
from io import StringIO

ry = YAML()
ry.default_flow_style = False
ry.indent(mapping=2, sequence=4, offset=2)

data = json.loads(json_data)
stream = StringIO()
ry.dump(data, stream)
print(stream.getvalue())

Important: Always use yaml.safe_load() instead of yaml.load(). The latter can execute arbitrary Python code from specially crafted YAML files, which is a serious security vulnerability.

Go

Go's most popular YAML library is gopkg.in/yaml.v3:

package main

import (
    "encoding/json"
    "fmt"
    "log"

    "gopkg.in/yaml.v3"
)

func main() {
    // JSON to YAML
    jsonData := []byte(`{
        "apiVersion": "v1",
        "kind": "ConfigMap",
        "metadata": {
            "name": "app-config"
        },
        "data": {
            "database_url": "postgres://localhost:5432/mydb",
            "cache_ttl": "300",
            "debug": "false"
        }
    }`)

    var obj map[string]interface{}
    if err := json.Unmarshal(jsonData, &obj); err != nil {
        log.Fatal(err)
    }

    yamlData, err := yaml.Marshal(obj)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(string(yamlData))

    // YAML to JSON
    yamlInput := []byte(`
name: my-service
replicas: 3
ports:
  - 8080
  - 8443
env:
  LOG_LEVEL: info
  TIMEOUT: "30s"
`)

    var result map[string]interface{}
    if err := yaml.Unmarshal(yamlInput, &result); err != nil {
        log.Fatal(err)
    }

    jsonOutput, err := json.MarshalIndent(result, "", "  ")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(string(jsonOutput))
}

Ruby

Ruby includes YAML support in its standard library via Psych:

require 'json'
require 'yaml'

# JSON to YAML
json_string = '{
  "name": "rails-app",
  "database": {
    "adapter": "postgresql",
    "host": "localhost",
    "port": 5432,
    "pool": 25
  },
  "redis": {
    "url": "redis://localhost:6379/0",
    "timeout": 5
  }
}'

data = JSON.parse(json_string)
yaml_output = data.to_yaml
puts yaml_output

# YAML to JSON
yaml_data = YAML.safe_load(File.read('config.yaml'))
json_output = JSON.pretty_generate(yaml_data)
puts json_output

# Fine-grained control with Psych
yaml_custom = Psych.dump(data,
  indentation: 2,
  line_width: 120
)
puts yaml_custom

Format Your JSON First

Minified JSON is hard to review before conversion. Use our JSON Formatter to pretty-print and organize your JSON data before converting to YAML.

6. Command-Line Tools for Conversion

Command-line tools are indispensable for scripting, CI/CD pipelines, and quick one-off conversions. Here are the best options.

yq: The YAML Swiss Army Knife

yq (by Mike Farah) is the go-to CLI tool for YAML manipulation. It mirrors jq's syntax and works seamlessly with both JSON and YAML:

# Install
brew install yq                     # macOS
sudo snap install yq                # Ubuntu
choco install yq                    # Windows

# JSON to YAML
yq -P input.json                    # Pretty-print as YAML
yq -P '.' input.json > output.yaml  # Save to file

# YAML to JSON
yq -o=json input.yaml               # Output as JSON
yq -o=json '.' input.yaml > output.json

# Convert from stdin (pipe from curl, etc.)
curl -s https://api.example.com/data | yq -P

# Extract specific fields during conversion
yq -P '.metadata.name' input.json

# Merge multiple YAML files
yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' \
  base.yaml override.yaml

Python One-Liners

If Python is installed (it usually is), you can convert without installing any extra tools:

# JSON to YAML (requires PyYAML: pip install pyyaml)
python3 -c "
import sys, json, yaml
print(yaml.dump(json.load(sys.stdin), default_flow_style=False))
" < input.json > output.yaml

# YAML to JSON
python3 -c "
import sys, json, yaml
print(json.dumps(yaml.safe_load(sys.stdin), indent=2))
" < input.yaml > output.json

# Inline conversion from a string
echo '{"name":"app","port":3000}' | python3 -c "
import sys, json, yaml
print(yaml.dump(json.load(sys.stdin), default_flow_style=False))
"

Ruby One-Liners

Ruby's standard library includes both JSON and YAML, so no gem installation is needed:

# JSON to YAML
ruby -ryaml -rjson -e \
  'puts YAML.dump(JSON.parse(STDIN.read))' < input.json

# YAML to JSON
ruby -ryaml -rjson -e \
  'puts JSON.pretty_generate(YAML.safe_load(STDIN.read))' < input.yaml

jq + yq Pipeline

Combine jq for JSON manipulation with yq for YAML output:

# Extract and transform JSON, output as YAML
cat data.json | jq '{name: .metadata.name, replicas: .spec.replicas}' | yq -P

# Process multiple JSON files into a single YAML
jq -s '.' file1.json file2.json | yq -P

# Convert Kubernetes JSON output to clean YAML
kubectl get deployment web -o json | \
  jq 'del(.metadata.managedFields, .status)' | \
  yq -P

Node.js One-Liner

# Requires: npm install -g js-yaml
js-yaml input.json > output.yaml

# Or using npx (no global install)
npx js-yaml input.json

Comparison of CLI Tools

Tool Install Size Speed Best For
yq ~10 MB (single binary) Fast General-purpose YAML/JSON conversion and manipulation
python3 -c 0 (uses system Python) Moderate Environments where Python is pre-installed
ruby -e 0 (uses system Ruby) Moderate Ruby-centric environments (Rails, Chef)
js-yaml CLI ~1 MB (npm package) Fast Node.js projects

7. YAML Best Practices for Config Files

Once you have converted your JSON data to YAML, follow these best practices to write clean, maintainable YAML configuration files.

1. Use Consistent Indentation (2 Spaces)

The YAML community has standardized on 2-space indentation. Kubernetes, Docker Compose, GitHub Actions, and most other tools expect it:

# Good: 2-space indent
services:
  web:
    image: nginx
    ports:
      - "80:80"

# Bad: tabs or inconsistent spacing
services:
    web:
        image: nginx

2. Quote Strings That Look Like Other Types

Prevent type coercion bugs by quoting ambiguous values:

# Always quote these:
version: "1.0"        # Prevents float interpretation
country: "NO"         # Prevents boolean false
zip: "01234"          # Prevents octal interpretation
enabled: "yes"        # Prevents boolean true (YAML 1.1)
timestamp: "12:30"    # Prevents sexagesimal number

3. Add Comments Generously

Comments are YAML's biggest advantage over JSON. Use them to explain non-obvious configuration choices:

# Maximum number of worker processes.
# Set to CPU count for compute-bound workloads,
# or higher for I/O-bound workloads.
workers: 4

# Connection timeout in seconds.
# Increase this if you see 504 errors under load.
timeout: 30

4. Use Block Style for Readability

Prefer block style (one item per line) over flow style (inline) for anything more than 2-3 simple items:

# Good: block style for longer lists
environment:
  - NODE_ENV=production
  - LOG_LEVEL=info
  - DATABASE_URL=postgres://db:5432/app
  - REDIS_URL=redis://cache:6379

# Acceptable: flow style for very short lists
ports: [80, 443]

5. Avoid Deeply Nested Structures

If your YAML is nested more than 4-5 levels deep, consider restructuring. Deep nesting makes files hard to read and prone to indentation errors:

# Too deep - hard to maintain
app:
  server:
    http:
      routes:
        api:
          v1:
            users:
              endpoint: /api/v1/users

# Better - flatter structure
routes:
  api_v1_users:
    path: /api/v1/users
    method: GET

6. Use Anchors for DRY Configuration

When multiple sections share the same values, use anchors to avoid repetition:

x-logging: &logging
  driver: json-file
  options:
    max-size: "10m"
    max-file: "3"

services:
  web:
    image: nginx
    logging: *logging

  api:
    image: node:20
    logging: *logging

7. Use Multi-line Strings for Long Values

# Use literal block (|) for preserving newlines
nginx_config: |
  server {
      listen 80;
      server_name example.com;
      location / {
          proxy_pass http://backend:3000;
      }
  }

# Use folded block (>) for paragraphs
description: >
  This application provides a REST API for managing
  user accounts, authentication tokens, and session
  data across multiple microservices.

8. Validate Before Deploying

Always validate YAML files before using them in production. A single indentation error can cause a deployment failure:

# Using yq to validate
yq '.' config.yaml > /dev/null && echo "Valid" || echo "Invalid"

# Using Python
python3 -c "import yaml; yaml.safe_load(open('config.yaml'))"

# Using yamllint (most thorough)
pip install yamllint
yamllint config.yaml

Validate YAML Online

Paste your YAML into our YAML Validator for instant syntax checking with detailed error messages and line numbers.

8. When to Use JSON vs YAML

JSON and YAML each have strengths that make them better suited for different use cases. Choosing the wrong format creates friction; choosing the right one makes your workflow smoother.

Use JSON When:

Use YAML When:

Decision Matrix

Criterion JSON YAML
Parse speed Faster (simpler grammar) Slower (complex grammar)
Human readability Good (with pretty-print) Excellent (minimal syntax noise)
Human writability Moderate (brackets, quotes required) Excellent (minimal ceremony)
Comments Not supported Supported
Type safety Explicit (no coercion) Implicit (type inference can surprise)
Tooling maturity Excellent (ubiquitous) Good (growing)
File size Larger (syntax overhead) Smaller (less punctuation)
Security Safe (JSON.parse is sandboxed) Risky if using unsafe loaders

For a deeper comparison including TOML, read our JSON vs YAML vs TOML guide. You can also validate your TOML files with our TOML Validator.

9. Real-World Examples

Let's walk through real-world configuration files that you encounter every day, showing the JSON and YAML representations side by side.

Kubernetes Deployment

Kubernetes is where most developers first encounter YAML. The API server speaks JSON, but manifests are almost always written in YAML:

JSON
{
  "apiVersion": "apps/v1",
  "kind": "Deployment",
  "metadata": {
    "name": "web-app",
    "labels": {
      "app": "web"
    }
  },
  "spec": {
    "replicas": 3,
    "selector": {
      "matchLabels": {
        "app": "web"
      }
    },
    "template": {
      "metadata": {
        "labels": {
          "app": "web"
        }
      },
      "spec": {
        "containers": [
          {
            "name": "nginx",
            "image": "nginx:1.27",
            "ports": [
              {
                "containerPort": 80
              }
            ]
          }
        ]
      }
    }
  }
}
YAML
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  labels:
    app: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          ports:
            - containerPort: 80

The YAML version is 18 lines versus 35 lines of JSON. With Kubernetes manifests often containing hundreds of lines, this reduction in visual clutter is significant. The ability to add comments explaining resource limits, environment variables, and volume mounts makes YAML the clear winner here.

Docker Compose

Docker Compose files showcase YAML's anchors and merge keys:

# docker-compose.yaml
x-common-env: &common-env
  LOG_LEVEL: info
  TZ: UTC

x-healthcheck: &healthcheck
  interval: 30s
  timeout: 10s
  retries: 3

services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    environment:
      <<: *common-env
      WORKER_PROCESSES: "4"
    healthcheck:
      <<: *healthcheck
      test: ["CMD", "curl", "-f", "http://localhost/health"]
    depends_on:
      api:
        condition: service_healthy

  api:
    build:
      context: ./api
      dockerfile: Dockerfile
    environment:
      <<: *common-env
      DATABASE_URL: postgres://db:5432/app
      REDIS_URL: redis://cache:6379
    healthcheck:
      <<: *healthcheck
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]

  db:
    image: postgres:16
    environment:
      POSTGRES_DB: app
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: secret  # Use Docker secrets in production
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      <<: *healthcheck
      test: ["CMD-SHELL", "pg_isready -U admin"]

volumes:
  pgdata:

The &common-env anchor and *common-env alias pattern keeps environment variables consistent across services without duplication. This has no JSON equivalent.

GitHub Actions CI/CD

# .github/workflows/ci.yaml
name: CI Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

# Cancel in-progress runs for the same branch
concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20, 22]

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Run linter
        run: npm run lint

      - name: Run tests
        run: npm test -- --coverage

      - name: Upload coverage
        if: matrix.node-version == 20
        uses: codecov/codecov-action@v4

  deploy:
    needs: test
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to production
        run: |
          echo "Deploying to production..."
          ./scripts/deploy.sh

Notice the multi-line string (run: |) for the deploy script. In JSON, those newlines would need \n escaping, making the config much harder to read and edit.

Ansible Playbook

# deploy-app.yaml
---
- name: Deploy web application
  hosts: webservers
  become: true
  vars:
    app_name: myapp
    app_port: 3000
    node_version: "20"  # Quoted to prevent float interpretation

  tasks:
    - name: Install Node.js
      apt:
        name: "nodejs={{ node_version }}.*"
        state: present
        update_cache: true

    - name: Clone repository
      git:
        repo: https://github.com/org/myapp.git
        dest: "/opt/{{ app_name }}"
        version: main

    - name: Install npm dependencies
      npm:
        path: "/opt/{{ app_name }}"
        production: true

    - name: Create systemd service
      template:
        src: app.service.j2
        dest: "/etc/systemd/system/{{ app_name }}.service"
      notify: restart app

  handlers:
    - name: restart app
      systemd:
        name: "{{ app_name }}"
        state: restarted
        daemon_reload: true

package.json vs pubspec.yaml

Some ecosystems use JSON (Node.js), while others use YAML (Flutter/Dart). The same dependency information looks different in each:

package.json (Node.js)
{
  "name": "my-app",
  "version": "2.1.0",
  "description": "A web app",
  "scripts": {
    "start": "node server.js",
    "test": "jest --coverage",
    "build": "webpack --mode production"
  },
  "dependencies": {
    "express": "^4.18.0",
    "pg": "^8.11.0"
  },
  "devDependencies": {
    "jest": "^29.7.0",
    "webpack": "^5.90.0"
  }
}
pubspec.yaml (Flutter)
name: my_app
version: "2.1.0"  # Quoted!
description: A mobile app

# Production dependencies
dependencies:
  flutter:
    sdk: flutter
  http: ^1.2.0
  provider: ^6.1.0

# Development dependencies
dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^3.0.0

Notice how the YAML version can include comments explaining dependency groups, while the JSON version cannot.

GitLab CI/CD

# .gitlab-ci.yml
stages:
  - build
  - test
  - deploy

# Shared configuration using YAML anchors
.default_rules: &default_rules
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

build:
  stage: build
  image: node:20-alpine
  <<: *default_rules
  script:
    - npm ci --cache .npm
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 hour
  cache:
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      - .npm/

test:
  stage: test
  image: node:20-alpine
  <<: *default_rules
  script:
    - npm ci --cache .npm
    - npm test
  coverage: '/Statements\s*:\s*(\d+\.?\d*)%/'

deploy_production:
  stage: deploy
  image: alpine:latest
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      when: manual
  script:
    - apk add --no-cache curl
    - |
      curl -X POST "$DEPLOY_WEBHOOK" \
        -H "Authorization: Bearer $DEPLOY_TOKEN" \
        -d "{\"version\": \"$CI_COMMIT_SHA\"}"
  environment:
    name: production
    url: https://myapp.example.com

The .default_rules hidden key (YAML keys starting with a dot are ignored by GitLab CI) combined with the anchor pattern is a powerful way to share configuration across jobs.

Conclusion

JSON and YAML are complementary formats, not competitors. JSON excels at machine-to-machine communication with its strict, unambiguous syntax and blazing-fast parsers. YAML excels at human-to-machine communication with its clean syntax, comment support, and powerful features like anchors and multi-line strings.

The key takeaways for converting between them:

With the knowledge from this guide, you can confidently convert between JSON and YAML in any context, whether you are writing Kubernetes manifests, configuring CI/CD pipelines, or building tools that bridge the gap between APIs and configuration files.

Try Our JSON to YAML Converter

Put this knowledge into practice with our JSON to YAML Converter. Paste your JSON, get clean YAML output instantly. You can also validate the result with our YAML Validator and format your source with the JSON Formatter.