FormatArc JSON to YAML conversion resultFormatArc JSON to YAML conversion result
Published: 2026-03-15Updated: 2026-06-04

Convert JSON to YAML — Kubernetes, Docker Compose, Ansible (Browser & CLI)

Convert JSON to YAML for Kubernetes manifests, Docker Compose, Helm values, and Ansible playbooks. Browser tool (no upload), yq, Python, Node.js, Go. Preserves key order, handles block scalars, multi-document output, string quoting, and the Norway problem.

TL;DR — pick a method in 10 seconds

  • Need it right now, no installFormatArc JSON to YAML (browser-side, no upload, validates JSON, preserves key order)
  • CLI / one-lineryq -P '.' file.json (the DevOps default, -P for block style)
  • Python script with key orderyaml.dump(data, sort_keys=False, default_flow_style=False, allow_unicode=True)
  • Node.js applicationjs-yaml yaml.dump(data, { lineWidth: -1 })
  • Go servicegopkg.in/yaml.v3 yaml.Marshal
  • Kubernetes round-tripkubectl get ... -o json | jq '...' | yq -P '.'
  • From JSON array to multi-document YAMLyq -P '.[]' --split-exp 'true' json.array.json
Method Setup Preserves key order Block scalars (|) Multi-doc output Comments insertion
FormatArc browser None Yes Yes No No (you add later)
yq -P '.' brew install yq Yes Yes Yes (split-exp) No
Python yaml.dump (PyYAML) pip install pyyaml Needs sort_keys=False Yes Manual --- separator No
Python ruamel.yaml pip install ruamel.yaml Yes Yes Yes Yes (manual API)
Node js-yaml npm install js-yaml Yes Yes Manual No
Go yaml.Marshal go get gopkg.in/yaml.v3 Yes (for yaml.Node) Yes Encoder loop No

Converting JSON to YAML is structurally trivial — both formats represent the same data model. The hard parts are preserving key order, deciding when to quote strings, handling multi-line strings, splitting JSON arrays into multi-document YAML, and avoiding the Norway problem in the output.

Why convert JSON to YAML?

JSON and YAML can represent the same data structures — maps, lists, strings, numbers, booleans, and null. They are functionally interchangeable. The reason to prefer one over the other usually comes down to context and readability.

Kubernetes and container orchestration

Kubernetes accepts both JSON and YAML for resource definitions, but the ecosystem overwhelmingly uses YAML. Documentation, tutorials, blog posts, and Stack Overflow answers are almost exclusively YAML. If you generate a resource definition programmatically (which often produces JSON), converting it to YAML makes it consistent with everything else in your cluster configuration and easier to review in pull requests.

kubectl even has a built-in mode for this:

kubectl get deployment web-app -o yaml > deployment.yaml

But when the source of truth is JSON (an admission controller webhook response, a Terraform Kubernetes provider output, a custom operator's reconciliation result), explicit conversion is required.

Docker Compose and Helm values

Docker Compose files are YAML by definition. If you have service configuration stored as JSON (perhaps from a configuration management API or a service mesh control plane), you need YAML before it can be used as a Compose file.

Helm chart values files are YAML. When you fetch values from an external source like Vault or AWS Parameter Store (which return JSON), convert to YAML before passing to helm install -f values.yaml.

Ansible playbooks

Ansible playbooks and inventories are YAML. Output from cloud APIs, CMDB systems, or asset databases typically arrives as JSON. Conversion to YAML is the first step before using it in playbooks.

Readability and editing

YAML is more human-friendly for configuration files. It eliminates the visual noise of curly braces, square brackets, and commas. Compare:

JSON:

{
  "server": {
    "host": "0.0.0.0",
    "port": 8080,
    "workers": 4,
    "logging": {
      "level": "info",
      "format": "json"
    }
  }
}

YAML:

server:
  host: 0.0.0.0
  port: 8080
  workers: 4
  logging:
    level: info
    format: json

The YAML version has fewer characters, less punctuation, and the hierarchical structure is communicated through indentation. For files that humans read and edit regularly, this matters.

Adding comments

JSON does not support comments. YAML does. If you need to annotate a configuration file — explaining why a value is set a certain way, documenting allowed values, or leaving notes for your team — YAML lets you do that directly:

server:
  host: 0.0.0.0
  port: 8080
  # Increase workers for production; 4 is fine for staging
  workers: 4

Converting JSON to YAML is often the first step before adding these kinds of annotations. The comments themselves cannot come from JSON — JSON has no comment syntax. See How to add comments to JSON for workarounds if you must keep the source as JSON.

When you need JSON → YAML: 4 concrete scenarios

Scenario 1: Kubernetes API → manifest in Git

The Kubernetes API returns JSON. You want to commit the deployment to your GitOps repo as YAML:

kubectl get deployment web-app -o json | yq -P '.' > deployment.yaml
git add deployment.yaml && git commit -m "Capture web-app current state"

Scenario 2: Helm values from Vault

Vault returns secrets as JSON. Helm expects YAML values files:

vault read -format=json secret/prod/app | jq '.data.data' | yq -P '.' > values.yaml
helm upgrade --install app ./chart -f values.yaml

Scenario 3: Docker Compose from a control plane API

A service mesh control plane returns service definitions as JSON. Docker Compose needs YAML:

curl -s https://control-plane/services | jq '.' | yq -P '.' > docker-compose.yml
docker compose up -d

Scenario 4: Ansible inventory from a CMDB

Most CMDBs (Device42, ServiceNow, NetBox) return JSON over REST. Ansible inventories are YAML:

curl -s https://cmdb/hosts | jq '.hosts' | yq -P '.' > inventory.yaml
ansible-playbook -i inventory.yaml site.yaml

In all four scenarios, the conversion is one pipeline step and easily scriptable.

Browser-side vs cloud converters: why uploading config files is risky

Many online JSON-to-YAML converters claim "client-side processing" but actually POST your data to their server. For configuration files this is dangerous because configs often contain:

  • API tokens and credentials in secret: blocks
  • Database connection strings with passwords
  • Cloud provider access keys (IAM roles, service account JSON)
  • Internal hostnames and infrastructure layout
  • Encryption keys and TLS certificates

Even if the service claims not to log uploads, a single misconfiguration on their side leaks your secrets. The safe approach is browser-side conversion that genuinely never sends data to a server.

This is not hypothetical. In November 2025, security firm watchTowr reported that JSONFormatter and CodeBeautify — two of the most popular online formatting and conversion sites — had years of saved user submissions publicly browsable through their "Recent Links" feature. Researchers collected over 80,000 entries (5GB+), including Active Directory credentials, database and cloud access keys, private keys, CI/CD secrets, JWT and API tokens, payment gateway credentials, and full AWS Secrets Manager exports, affecting organizations across government, banking, healthcare, and aerospace (watchTowr's disclosure). Anything you paste into a converter that stores or transmits your input can be exposed the same way.

To verify: open the converter, open DevTools → Network, disable network, paste your JSON. If the tool keeps working, it is browser-side. If it hangs or errors, it is cloud-based.

The FormatArc JSON to YAML tool keeps working with network disabled. So does yq locally. So does any Python / Node / Go script on your laptop.

For Kubernetes Secrets, Helm values containing API tokens, or any config with credentials, always use a local converter.

For a full method to verify whether an online converter is safe before pasting, see Are online JSON converters safe?.

Method 1: FormatArc browser tool (no upload)

The JSON to YAML tool converts your JSON to properly formatted YAML in seconds, entirely in your browser.

  1. Open the JSON to YAML converter.
  2. Paste your JSON into the left panel.
  3. Click Convert.

JSON to YAML conversion resultJSON to YAML conversion result

The tool validates your JSON before converting, so if there is a syntax error — a missing comma, a trailing brace, unquoted keys — it reports the problem with a line number. This makes it useful as a JSON validator even when you do not need YAML output. See JSON parse error troubleshooting for common JSON syntax problems.

Since the conversion runs in your browser, you can safely paste credentials, internal configurations, or any other sensitive data without it being transmitted anywhere.

The output uses 2-space indentation (the Kubernetes standard), preserves key order from the input JSON, and uses block style by default (so it looks like normal YAML, not flow style with braces).

Method 2: yq (the DevOps default)

yq handles JSON-to-YAML conversion just as cleanly as the reverse direction:

# Basic conversion
yq -P '.' input.json > output.yaml

# Pipe from stdin
cat config.json | yq -P '.' > output.yaml

# Pretty (block style); without -P, output may use flow style
yq -P '.' input.json

# Set indent (default 2)
yq -P -I=4 '.' input.json

# Extract sub-tree and convert
yq -P '.spec.template' deployment.json

# Multi-document output from JSON array
yq -P '.[]' --split-exp 'true' kubernetes-list.json

The -P flag is short for --prettyPrint which forces YAML block style. Without it, yq may output flow style (which looks like JSON with fewer quotes) for nested structures.

Install:

# macOS
brew install yq

# Linux (binary)
sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
sudo chmod +x /usr/local/bin/yq

# Docker
docker run --rm -i mikefarah/yq -P '.' < input.json > output.yaml

yq with jq for filtering during conversion

# Extract just spec.template from a deployment and convert to YAML
jq '.spec.template' deployment.json | yq -P '.'

# Or do it all in yq
yq -P '.spec.template' deployment.json

Method 3: Python (yaml.dump and ruamel.yaml)

PyYAML basic usage

import json
import yaml

with open("config.json") as f:
    data = json.load(f)

with open("config.yaml", "w") as f:
    yaml.dump(
        data,
        f,
        default_flow_style=False,   # block style (the usual YAML look)
        allow_unicode=True,          # do not escape Unicode characters
        sort_keys=False,             # preserve original key order
    )

The three options that matter:

  • default_flow_style=False — produces normal YAML, not the compact {a: 1, b: 2} form
  • allow_unicode=True — keeps Japanese, accented Latin, emoji as-is rather than \uXXXX escapes
  • sort_keys=False — preserves the JSON input order (essential for Kubernetes where apiVersion must come before kind)

One-liner

python3 -c 'import sys, json, yaml; yaml.dump(json.load(sys.stdin), sys.stdout, default_flow_style=False, allow_unicode=True, sort_keys=False)' < input.json > output.yaml

ruamel.yaml for round-trip with comments

If you need to add comments to the generated YAML or preserve specific formatting:

from ruamel.yaml import YAML
import json

yaml_writer = YAML()
yaml_writer.default_flow_style = False
yaml_writer.preserve_quotes = True
yaml_writer.indent(mapping=2, sequence=4, offset=2)

with open("config.json") as fin:
    data = json.load(fin)

# Add comments programmatically
data.yaml_set_comment_before_after_key("server", before="Web server configuration")

with open("config.yaml", "w") as fout:
    yaml_writer.dump(data, fout)

ruamel.yaml is the right choice when comments matter — JSON has no comments, so any annotation must be added during or after conversion.

Method 4: Node.js (js-yaml)

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

const data = JSON.parse(fs.readFileSync("config.json", "utf8"));
const ymlText = yaml.dump(data, {
  lineWidth: -1,    // never wrap long lines
  noRefs: true,     // do not emit YAML anchors / aliases
  sortKeys: false,  // preserve input order
  quotingType: '"', // prefer double quotes when quoting is needed
});

fs.writeFileSync("config.yaml", ymlText);

lineWidth: -1 prevents js-yaml from wrapping long lines, which can cause unexpected line breaks in URLs and other long values. noRefs: true disables anchor generation — useful when the consumer is kubectl or another tool that does not support YAML anchors.

Method 5: Go (gopkg.in/yaml.v3)

package main

import (
    "encoding/json"
    "fmt"
    "os"

    "gopkg.in/yaml.v3"
)

func main() {
    data, _ := os.ReadFile("config.json")
    var obj interface{}
    json.Unmarshal(data, &obj)
    out, _ := yaml.Marshal(obj)
    fmt.Print(string(out))
}

For key order preservation in Go, use yaml.Node directly instead of interface{}json.Unmarshal into map[string]interface{} does not preserve order (Go maps are intentionally randomized). For Kubernetes manifests where order matters, this is a problem:

// Use yaml.Node for explicit ordering
var node yaml.Node
yaml.Unmarshal(data, &node)
out, _ := yaml.Marshal(&node)

Alternatively, since Go's encoding/json sorts map keys alphabetically, use a third-party library like github.com/iancoleman/orderedmap to preserve insertion order.

Kubernetes Deployment example: JSON to YAML round trip

A complete real-world example. Start with a JSON deployment definition:

{
  "apiVersion": "apps/v1",
  "kind": "Deployment",
  "metadata": {
    "name": "web-app",
    "labels": { "app": "web", "tier": "frontend" }
  },
  "spec": {
    "replicas": 3,
    "selector": { "matchLabels": { "app": "web" } },
    "template": {
      "metadata": { "labels": { "app": "web" } },
      "spec": {
        "containers": [{
          "name": "nginx",
          "image": "nginx:1.25",
          "ports": [{ "containerPort": 80 }],
          "env": [
            { "name": "LOG_LEVEL", "value": "info" }
          ]
        }]
      }
    }
  }
}

Convert with yq:

yq -P '.' deployment.json

Output:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  labels:
    app: web
    tier: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: nginx
          image: nginx:1.25
          ports:
            - containerPort: 80
          env:
            - name: LOG_LEVEL
              value: info

Note that apiVersion stays before kind, before metadata — key order is preserved. With PyYAML and sort_keys=True (the default), the output would be alphabetical and kubectl apply would still work, but the diff against the upstream Kubernetes example becomes harder to review.

Apply: kubectl apply -f deployment.yaml.

String quoting in YAML: when "8080" becomes 8080

YAML has complex rules about when strings need quotes. JSON-to-YAML converters apply these rules to the output, which can cause unexpected type changes.

Numbers-as-strings get unquoted

If your JSON has "8080" as a string (because the API returned it that way), converters may unquote it in YAML, turning it into a number:

JSON:

{ "port": "8080" }

After conversion (some tools):

port: "8080"   # good: preserved as string

Or:

port: 8080     # bad: became a number

yq v4 preserves the original type (string stays string). PyYAML with default settings also preserves it. js-yaml preserves it. The risk is highest with hand-written conversion code that uses heuristics.

To force quoting in PyYAML, wrap values:

class QuotedString(str): pass

def represent_quoted(dumper, data):
    return dumper.represent_scalar("tag:yaml.org,2002:str", str(data), style='"')

yaml.add_representer(QuotedString, represent_quoted)

Strings that look like booleans

{ "active": "yes" }

After conversion (with YAML 1.1 schema):

active: yes    # interpreted as boolean true on the next round trip

A good converter quotes these:

active: "yes"

If you control the source JSON, prefer true / false / null (JSON's native types) over strings like "yes" / "no" — this sidesteps the Norway problem entirely.

Strings starting with special characters

YAML special characters (*, &, !, {, [, >, |, #, @, \``, leading -`) need quoting when they start a value:

{ "tag": "*production*" }

Becomes:

tag: "*production*"   # quoted because of leading *

All major converters handle this correctly. The risk only emerges with hand-rolled converters.

Block scalars: | vs > for multiline strings

JSON represents multi-line strings with \n escape sequences. When converted to YAML, a good converter uses block scalar notation:

JSON:

{
  "description": "Line one\nLine two\nLine three"
}

A converter has three reasonable choices in YAML:

Literal block (|) — preserves newlines exactly

description: |
  Line one
  Line two
  Line three

Each newline in the source becomes a newline in the output string. This is what you want for log messages, certificate PEM blocks, code snippets embedded in config.

Folded block (>) — joins lines with spaces

description: >
  Line one
  Line two
  Line three

This produces the string Line one Line two Line three (newlines become spaces, blank lines become single newlines). Good for long prose that should display as a paragraph.

Quoted single-line

description: "Line one\nLine two\nLine three"

The literal \n escape sequence inside a double-quoted string. Works but is hard to read for more than 2-3 lines.

yq -P defaults to | (literal) for any string containing newlines. PyYAML uses | when default_style=None (the default) and the string has a newline. js-yaml uses | for newline strings by default.

To force a specific style in PyYAML:

yaml.dump(data, default_style='|')   # all strings as literal blocks
yaml.dump(data, default_style='"')   # all strings double-quoted

For Kubernetes ConfigMaps containing multi-line scripts or PEM certificates, | is the right choice and is what kubectl create configmap --from-file produces.

Key ordering: preserving apiVersionkindmetadata

JSON objects and YAML mappings are both technically unordered per spec. In practice, everyone expects keys in a meaningful order — especially Kubernetes resources where convention has apiVersion first, then kind, then metadata, then spec.

Most converters preserve source order:

  • yq (Go yaml.v3): preserves order via internal yaml.Node
  • PyYAML with sort_keys=False: preserves order (default is sort_keys=True which alphabetizes — set it explicitly)
  • js-yaml with sortKeys: false: preserves order
  • ruamel.yaml: preserves order by default

Watch out for:

  • PyYAML's default sort_keys=True — alphabetizes everything, putting apiVersion after kind and breaking the convention
  • Go's interface{} unmarshalling — randomizes order (use yaml.Node instead)
  • Hand-rolled converters that build a map[string]string and emit it — Go maps are intentionally random

If you commit YAML to Git, key randomization causes massive diff churn even when the data is unchanged. Always verify your converter preserves order before adopting it for GitOps workflows.

From JSON array to multi-document YAML

A common pattern: you have a JSON array of Kubernetes resources, and you want a single YAML file with multiple ----separated documents (one per resource).

With yq

yq -P '.[]' --split-exp 'true' kubernetes-list.json

This outputs:

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
---
apiVersion: v1
kind: Service
metadata:
  name: app-service

Save to a file: yq -P '.[]' --split-exp 'true' kubernetes-list.json > resources.yaml. The file is now kubectl apply -f resources.yaml-ready.

With Python

import json
import yaml

with open("list.json") as f:
    items = json.load(f)["items"]   # adjust path to your array

with open("resources.yaml", "w") as f:
    yaml.dump_all(
        items,
        f,
        default_flow_style=False,
        allow_unicode=True,
        sort_keys=False,
    )

yaml.dump_all emits multiple documents separated by ---.

With Node.js

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

const items = JSON.parse(fs.readFileSync("list.json", "utf8")).items;
const ymlText = items.map(item => yaml.dump(item, { lineWidth: -1 })).join("---\n");

fs.writeFileSync("resources.yaml", ymlText);

Manual joining with ---\n since js-yaml does not have a built-in dumpAll.

CI/CD pipeline integration

GitHub Actions

name: Convert JSON config to YAML for deployment
on:
  push:
    paths: ["config/*.json"]

jobs:
  convert:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install yq
        run: |
          sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
          sudo chmod +x /usr/local/bin/yq
      - name: Convert all configs
        run: |
          for f in config/*.json; do
            yq -P '.' "$f" > "${f%.json}.yaml"
          done
      - name: Validate against Kubernetes
        run: |
          for f in config/*.yaml; do
            kubectl apply --dry-run=client -f "$f"
          done
      - uses: stefanzweifel/git-auto-commit-action@v5
        with:
          commit_message: "Auto-convert configs to YAML"

GitLab CI

generate-yaml:
  image: mikefarah/yq:latest
  stage: build
  script:
    - for f in config/*.json; do yq -P "." "$f" > "${f%.json}.yaml"; done
  artifacts:
    paths:
      - config/*.yaml

Jenkins (declarative)

pipeline {
  agent any
  stages {
    stage('Convert configs') {
      steps {
        sh 'for f in config/*.json; do yq -P "." "$f" > "${f%.json}.yaml"; done'
      }
    }
    stage('Validate') {
      steps {
        sh 'for f in config/*.yaml; do kubectl apply --dry-run=client -f "$f"; done'
      }
    }
  }
}

The full pipeline (convert + validate) adds less than 5 seconds to typical CI runs.

A practical workflow for Kubernetes

Here is the most common real-world scenario. You use kubectl to get a resource as JSON, modify it, save it as YAML, and commit to Git:

# Get the current deployment as JSON
kubectl get deployment web-app -o json > deployment.json

# Edit (or use jq for automated changes)
jq '.spec.replicas = 5' deployment.json > updated.json

# Convert to YAML for your Git repository
yq -P '.' updated.json > deployment.yaml

# Inspect, commit, push
git diff deployment.yaml
git add deployment.yaml && git commit -m "Scale web-app to 5 replicas"

Or in a single pipeline:

kubectl get deployment web-app -o json | jq '.spec.replicas = 5' | yq -P '.' > deployment.yaml

For quick modifications where you do not need scripting, paste the JSON into the JSON to YAML tool, copy the YAML output, and save it to your file.

Frequently asked questions

Why does port 8080 become a string in my YAML output?

It does not — YAML treats unquoted numbers as numbers. What sometimes happens is the opposite: a JSON string "8080" gets emitted as an unquoted YAML 8080, becoming a number on the next round trip. To preserve string type, ensure your converter quotes ambiguous values. yq v4 and modern PyYAML both handle this correctly.

Can I preserve YAML comments through a round trip?

No. JSON has no comments. If you convert JSON to YAML and add comments, those comments are lost the moment you convert back to JSON. Keep YAML as the source of truth if comments matter, and use JSON comments workarounds on the JSON side.

Why does PyYAML sort my keys alphabetically?

PyYAML's yaml.dump defaults to sort_keys=True. Set sort_keys=False to preserve the input order. This matters for Kubernetes resources where convention puts apiVersion first.

Can I convert Helm values.json to values.yaml?

Yes. The values structure is identical, just the format differs. Use yq -P '.' values.json > values.yaml. Verify the result with helm template ./chart -f values.yaml > rendered.yaml to confirm the manifests match.

Why is my YAML null shown as ~?

YAML has multiple representations for null: null, Null, NULL, ~, or simply an empty value. Different converters choose different representations. yq uses null, PyYAML defaults to nothing (empty), js-yaml uses null. All are equivalent — only cosmetic. To force a specific representation:

yaml.add_representer(type(None), lambda d, _: d.represent_scalar("tag:yaml.org,2002:null", "null"))

Are JSON anchors preserved in the YAML output?

JSON has no anchors. If you want to use YAML anchors (&anchor / *alias) in the output, you must add them manually after conversion or use a YAML-aware editor. ruamel.yaml supports programmatic anchor creation if you need to automate this.

How do I convert JSON to YAML without internet access?

Three local options: install yq (Go binary, single file, no dependencies), use a Python script with pyyaml, or use a Node.js script with js-yaml. The FormatArc JSON to YAML tool also works offline once the page is loaded — browser-side conversion means no further network access is needed.

Going the other direction

To convert YAML back to JSON — for API submission, debugging, or feeding to JSON-only tools — see How to convert YAML to JSON. The FormatArc YAML to JSON tool handles the reverse direction with the same ease.

Summary

Converting JSON to YAML comes up constantly in DevOps, infrastructure-as-code, and configuration management workflows. Five methods cover every situation:

  • No-install, sensitive data: FormatArc JSON to YAML — browser-side, preserves key order, line-numbered JSON errors.
  • CLI / pipelines: yq -P '.' — the DevOps default, supports filtering and multi-document output.
  • Python applications: yaml.dump(data, sort_keys=False, default_flow_style=False, allow_unicode=True).
  • Node.js applications: js-yaml yaml.dump with lineWidth: -1 and sortKeys: false.
  • Go services: gopkg.in/yaml.v3 Marshal, using yaml.Node for order preservation.

The four production-critical concerns: preserve key order (essential for Kubernetes), handle multi-line strings with block scalars (|), split JSON arrays into multi-document YAML when consumer expects it, and watch for unquoted strings that look like booleans or numbers triggering the Norway problem on the next round trip.