Converting Between JSON and YAML on the Command Line
fx reads YAML as input and can write YAML with the built-in YAML.stringify, so one tool handles both directions of the conversion. Install fx and follow the examples below.
Convert YAML to JSON
Pass --yaml and the . argument to print the document as pretty JSON:
cat config.yaml | fx --yaml .Input:
# service config
service:
name: billing-api
port: 8080
database:
host: db.internal
features:
- invoices
- refundsOutput:
{
"service": {
"name": "billing-api",
"port": 8080
},
"database": {
"host": "db.internal"
},
"features": [
"invoices",
"refunds"
]
}Files ending in .yaml or .yml are detected by extension, so fx config.yaml . works without the flag. With stdin you always need --yaml. Comments are dropped, since JSON has no comments. Save the result with fx config.yaml . > config.json.
Convert JSON to YAML
Pass YAML.stringify as an argument. It returns a string, and fx prints strings without quotes, so the output is plain YAML:
fx users.json YAML.stringifyInput:
[
{"id": 1, "name": "Alice", "role": "admin", "active": true},
{"id": 2, "name": "Bob", "role": "viewer", "active": false}
]Output:
- id: 1
name: Alice
role: admin
active: true
- id: 2
name: Bob
role: viewer
active: falseStrings that YAML would read as another type keep their quotes ("yes", "0755", "1.0"), and strings with newlines become |- block scalars, so converting to YAML and back to JSON gives you the same data. You can filter first: fx users.json '?.active' '@.name' YAML.stringify prints - Alice.
Convert docker-compose.yml or a Kubernetes Manifest to JSON
Compose files and manifests are ordinary YAML:
fx docker-compose.yml .Input:
services:
web:
image: nginx:1.27
ports:
- "8080:80"
db:
image: postgres:16Output:
{
"services": {
"web": {
"image": "nginx:1.27",
"ports": [ "8080:80" ]
},
"db": {
"image": "postgres:16"
}
}
}You can query a manifest the same way you query JSON, for example to get the container image from a Deployment:
fx deployment.yaml '.spec.template.spec.containers[0].image'registry.example.com/billing-api:1.4.0Edit a YAML Value and Write YAML Back
Change the parsed data with JavaScript, then run YAML.stringify as the last step. The expression x.field = value, x assigns the value and returns the whole document:
fx config.yaml 'x.service.port = 9090, x' 'x.features.push("exports"), x' YAML.stringify > config.new.yamlContents of config.new.yaml (key order is kept, comments are lost, list items move to the parent key's indent):
service:
name: billing-api
port: 9090
database:
host: db.internal
features:
- invoices
- refunds
- exportsWARNING
Do not redirect to the file you are reading (fx a.yaml ... > a.yaml). The shell empties the file before fx reads it. Write to a new file and move it into place afterwards.
Handle Multi-Document YAML (---)
fx splits a YAML stream on --- and handles each document as a separate value, the same way it handles newline-delimited JSON:
cat multi.yaml | fx --yaml '.kind'Input:
apiVersion: v1
kind: Service
metadata:
name: billing
---
apiVersion: v1
kind: ConfigMap
metadata:
name: billing-configOutput:
Service
ConfigMapTo keep only one document, return skip for the others: 'x.kind == "ConfigMap" ? x : skip'.
Running YAML.stringify on each document separately does not put the --- separators back. To get valid multi-document YAML, collect the documents into an array with --slurp and join them yourself:
cat multi.yaml | fx --yaml -s 'x => x.map(YAML.stringify).join("---\n")'apiVersion: v1
kind: Service
metadata:
name: billing
---
apiVersion: v1
kind: ConfigMap
metadata:
name: billing-configParse a YAML String Embedded in JSON
APIs sometimes return YAML as a string field, such as Helm values or CI configs. YAML.parse turns that string into an object:
fx release.json 'x.values = YAML.parse(x.values), x'Input:
{
"release": "billing-api",
"values": "replicas: 3\nimage:\n tag: 1.4.0\n"
}Output:
{
"release": "billing-api",
"values": {
"replicas": 3,
"image": {
"tag": "1.4.0"
}
}
}