Converting JSON to YAML: A Complete Practical Guide
Quick answer
💡To convert JSON to YAML, use js-yaml in Node.js with yaml.dump(jsonObj), the yq command-line tool with yq -y . file.json, or PyYAML in Python with yaml.dump(data). YAML 1.2 is a superset of JSON, so valid JSON is already valid YAML. Paste your JSON into ToolDock JSON Formatter first to ensure it is well-formed before converting.
Error symptoms
- ✕
YAML output contains unexpected !!python/object tags from PyYAML - ✕
String values like 'no', 'yes', 'on', 'off' become booleans in YAML 1.1 parsers - ✕
Multiline strings in YAML collapse to a single line when read back - ✕
Kubernetes manifests reject a converted YAML file due to type mismatches - ✕
yq produces slightly different output structure than js-yaml for the same input - ✕
YAML anchors and aliases created manually are silently dropped when round-tripping back to JSON
Common causes
- •Using PyYAML default Dumper which emits Python-specific type tags
- •Targeting a YAML 1.1 parser that treats unquoted yes/no/on/off as Boolean values
- •Not specifying a block style, so the dumper inlines nested structures on one line
- •Assuming JSON anchors or $ref references automatically become YAML anchors
- •Running yq without the -y flag and receiving JSON output instead of YAML
- •Relying on implicit type coercion rather than quoting ambiguous scalar values
When it happens
- •Writing Kubernetes manifests or Helm values files from existing JSON configuration
- •Migrating Docker Compose files that were generated programmatically as JSON
- •Converting package.json or tsconfig.json fields into a YAML CI pipeline definition
- •Sharing API schema files as YAML for human readability after initial JSON development
Examples and fixes
A Node.js script reads a JSON application configuration file and writes the YAML equivalent to disk. The dump call uses block style to keep the output readable.
Converting a JSON config file using js-yaml in Node.js
❌ Wrong
const fs = require('fs');
const yaml = require('js-yaml');
const rawJson = fs.readFileSync('app-config.json', 'utf8');
const configData = JSON.parse(rawJson);
// Missing options — produces flow style on a single line
const yamlOutput = yaml.dump(configData);
fs.writeFileSync('app-config.yaml', yamlOutput);
console.log('Wrote', yamlOutput.length, 'bytes');✅ Fixed
const fs = require('fs');
const yaml = require('js-yaml');
const rawJson = fs.readFileSync('app-config.json', 'utf8');
const configData = JSON.parse(rawJson);
const yamlOutput = yaml.dump(configData, {
indent: 2,
lineWidth: 120,
noRefs: true
});
fs.writeFileSync('app-config.yaml', yamlOutput, 'utf8');
console.log('Wrote', yamlOutput.split('\n').length, 'lines');The noRefs option prevents js-yaml from emitting YAML anchors when the same object appears multiple times, which can confuse parsers that do not support aliases. Setting indent to 2 and a reasonable lineWidth keeps the output readable. Without these options, the default dumper may produce deeply nested flow-style output that is valid YAML but hard for humans to edit.
A shell script converts every .json file in a directory to .yaml for use in a GitOps repository. The wrong version omits the -y flag and outputs JSON instead of YAML.
Converting JSON to YAML at the command line with yq
❌ Wrong
#!/usr/bin/env bash
# Missing -y flag — outputs JSON, not YAML
for jsonFile in configs/*.json; do
baseName=$(basename "$jsonFile" .json)
yq . "$jsonFile" > "configs/${baseName}.yaml"
echo "Converted $jsonFile"
done✅ Fixed
#!/usr/bin/env bash
for jsonFile in configs/*.json; do
baseName=$(basename "$jsonFile" .json)
yq -y . "$jsonFile" > "configs/${baseName}.yaml"
echo "Converted $jsonFile -> configs/${baseName}.yaml"
done
# To convert YAML back to JSON for verification:
# yq -j . configs/service.yamlThe yq tool by default outputs JSON. The -y flag switches output format to YAML. The reverse conversion from YAML to JSON uses the -j flag, which is useful for verifying that the round-trip preserves all field values. Always test round-trip fidelity when converting configuration files that drive deployments.
YAML 1.2 as a superset of JSON
The relationship between JSON and YAML is formally defined by the YAML 1.2 specification, which was published specifically to make JSON a strict subset of YAML. This means that any document conforming to RFC 8259 JSON is also a valid YAML 1.2 document and can be parsed by any compliant YAML 1.2 parser without modification. The practical implication is that you can start by dropping a JSON file into a YAML context and it will be accepted. The conversion from JSON to idiomatic YAML is then a formatting step, not a semantic one.
YAML 1.1, which predates 1.2, had a broader set of implicit type coercions and is not a strict superset of JSON. The key difference is that YAML 1.1 treats unquoted values such as yes, no, on, off, true, false, and null as Boolean or null types. This creates a problem when a JSON document contains a string field with one of those values without quoting. For example, a configuration file with a field set to the string 'no' will be read back as the Boolean false by a YAML 1.1 parser, silently changing its meaning. Go's gopkg.in/yaml.v2 library uses YAML 1.1 semantics by default, making this a real source of bugs in Kubernetes tooling.
When you convert JSON to YAML, you are preserving the data structure while changing the serialization format to something humans can edit more comfortably. YAML supports block scalars for multiline strings, comments that JSON forbids, anchors and aliases for referencing the same value in multiple places, and a more relaxed syntax for simple scalar values. These features make YAML the preferred format for configuration files such as Kubernetes manifests, GitHub Actions workflows, Docker Compose files, and Ansible playbooks.
Understanding the superset relationship also clarifies what is lost when you convert back from YAML to JSON. Comments, anchors, aliases, and YAML-specific type tags have no JSON equivalents. A round-trip through JSON will strip all of these features. This means that converting a hand-authored YAML file to JSON and back produces a semantically equivalent but informationally reduced document.
Converting JSON files with js-yaml and yq
The most widely used JavaScript library for JSON-to-YAML conversion is js-yaml, available on npm. Its yaml.dump function accepts a JavaScript value and returns a YAML string. Because JSON.parse already handles the deserialization step, the typical workflow is a two-step process: parse the JSON string into a JavaScript object, then pass that object to yaml.dump. The library's output is controlled by an options object that lets you set the indentation level, line width, whether to produce block style or flow style, and whether to suppress YAML anchors.
The noRefs option deserves special attention. When a JavaScript object contains the same nested object referenced from multiple properties, js-yaml detects this and by default emits YAML anchor and alias syntax to represent the shared reference. Most YAML parsers handle anchors correctly, but some tooling expects simple YAML documents without aliases. Setting noRefs to true causes js-yaml to duplicate the object value at each reference point rather than creating an alias, which produces a larger but more universally compatible document.
The yq command-line tool provides a convenient way to convert JSON to YAML in shell scripts. The -y flag instructs yq to emit YAML output instead of the default JSON. When piping from stdin, the invocation yq -y . reads from standard input and writes YAML to standard output. When reading from a file, yq -y . input.json writes the result to stdout, which you redirect to the target file. For automation tasks that process multiple files, yq is often the simplest choice because it handles both conversion and field selection in a single command.
For Python projects, PyYAML provides yaml.dump, but the default Dumper is not suitable for serializing arbitrary Python objects because it emits type tags such as !!python/object that are not portable across implementations. When converting data that originated as JSON, you should always use yaml.safe_dump or yaml.dump with Dumper=yaml.SafeDumper. The safe dumper only emits standard YAML types and never adds Python-specific tags. If you need comment preservation and safe round-tripping in Python, the ruamel.yaml library is the better choice because it maintains the structure of the original YAML document when modifying and re-serializing it.
Handling YAML type coercion pitfalls
Type coercion is the most dangerous aspect of JSON-to-YAML conversion when the resulting YAML will be consumed by a YAML 1.1 parser. The set of affected values includes the strings yes, no, true, false, on, off, null, and their mixed-case variants such as True, FALSE, and YES. If these values appear unquoted in a YAML document parsed by a 1.1-compliant library, they will be silently converted to their Boolean or null equivalents. A JSON string field with the value 'off' becomes the Boolean false, and a field with the value 'null' may become a null rather than the four-character string.
The safest mitigation is to quote these values explicitly in the generated YAML output. The js-yaml library can be configured to force quoting of ambiguous scalars, but it requires passing a custom schema or post-processing the output. The most reliable approach in production is to validate the converted YAML document by parsing it back with the same library your deployment target uses and comparing the result against the original JSON. If any field values differ after the round-trip, those fields need explicit quoting.
Numeric values also present a coercion risk. JSON numbers are untyped floating-point values, but YAML distinguishes between integers, floats, and octal or hexadecimal representations. A JSON value of 0755 would be interpreted as an octal number by YAML 1.1 parsers, which is almost never the intended meaning when it appears in a file permission configuration. JSON file mode values should always be expressed as strings or quoted in the YAML output.
The best long-term approach is to control which YAML version your tooling uses. If you are producing YAML for Kubernetes, use a YAML 1.2 parser on the client side during validation. If you are producing YAML for older tools, explicitly quote any string value that could be mistaken for a boolean or null. Using a schema-validated output step, such as running the generated YAML through kubeval or yamllint with strict settings, catches coercion errors before they reach a production deployment.
Anchors and aliases lost in JSON round-trips
YAML's anchor and alias mechanism allows a single value to be defined once and referenced in multiple places within the same document. An anchor is declared with an ampersand followed by a name, such as &defaultTimeout. Any later node in the document can reference that anchor with an asterisk, such as *defaultTimeout, and the parser will substitute the anchored value at that position. This feature is widely used in Kubernetes Helm charts and in CI pipeline definitions to avoid repeating the same configuration block.
JSON has no equivalent mechanism. Every value must be written out in full at every location where it appears. When you convert a YAML file containing anchors and aliases to JSON, the JSON representation is semantically equivalent because the parser expands all aliases before serialization, but the deduplication is lost. When you convert that JSON back to YAML, you get a document with all values written out explicitly and no anchors. This is a one-way information loss that cannot be reversed automatically.
If you need to maintain YAML anchor structure through a programmatic transformation, you must work entirely within the YAML domain and avoid the JSON round-trip. The ruamel.yaml library in Python is designed specifically for this use case. It preserves anchors, aliases, and comments when loading and re-dumping a YAML document, making it suitable for tooling that needs to modify YAML configuration files without destroying their structure.
For JavaScript projects, the yaml npm package (distinct from js-yaml) provides a document model that preserves YAML-specific constructs including anchors. When you parse a YAML string with yaml.parseDocument, you get a Document object rather than a plain JavaScript value. You can traverse and modify the document tree while preserving anchors, and then serialize it back to YAML with document.toString. This is the correct approach when writing tools that transform YAML configuration files as part of a developer workflow.
When YAML 1.1 treats strings as booleans
The boolean coercion problem in YAML 1.1 is subtle because it only triggers for a specific set of unquoted string values that were not problematic in JSON. A developer writing a configuration file in JSON can safely use the string value 'off' for a feature flag because JSON treats all string values as strings regardless of their content. When that same file is converted to YAML and consumed by a YAML 1.1 parser, the field value changes semantics without any error or warning.
The complete list of problematic values in YAML 1.1 includes: y, Y, yes, Yes, YES, n, N, no, No, NO, true, True, TRUE, false, False, FALSE, on, On, ON, off, Off, OFF. These values are all interpreted as booleans. Additionally, ~ and null and Null and NULL become null values. The canonical Go YAML library gopkg.in/yaml.v2 implements YAML 1.1, which means any Go-based Kubernetes tooling using that library will apply these coercions.
A practical example of this problem appears in language configuration files. A JSON configuration that maps country codes or language settings to behavior descriptors might contain a field for the Norwegian language code 'no'. When this configuration is converted to YAML and read by a YAML 1.1 parser, the value becomes the Boolean false. Similarly, an environment variable file might contain a field named 'environment' with the value 'on', which becomes the Boolean true after conversion.
Detecting this category of bug requires comparing field types, not just values, after a round-trip. A simple string comparison of the JSON and YAML parsed output will not catch this case because the Boolean false and the string 'no' both serialize to different representations. The correct validation step is to parse the YAML output, convert it back to JSON, parse that JSON, and then perform a deep strict equality check against the original JavaScript object, ensuring that typeof comparisons match as well as value comparisons. Any field where the type changes between input and output is a coercion victim that needs explicit quoting in the YAML.
Preserving type fidelity across the format boundary
Reliable JSON-to-YAML conversion in production environments requires more than calling yaml.dump on a parsed object. Type fidelity means that every field in the original JSON document has the same type and value after parsing the converted YAML document. Achieving this requires a validation step, the right library configuration, and awareness of the edge cases described above.
The most robust pattern for a Node.js conversion pipeline is to parse the JSON, dump it to YAML with js-yaml using the JSON_SCHEMA or CORE schema rather than the default schema, and then immediately parse the resulting YAML back with the same schema. Comparing the parsed YAML object against the original JavaScript object using a deep equality function reveals any discrepancy introduced by the conversion. The CORE schema aligns with YAML 1.2 and avoids the 1.1 boolean coercions.
For Kubernetes and other cloud-native workflows, the yq tool is often preferable to library-based conversion because it is designed specifically for YAML manipulation and understands the YAML 1.2 specification. Combining yq with yamllint and kubeval in a CI pipeline creates a validation chain that catches both formatting issues and schema violations before a manifest reaches a cluster.
When generating YAML programmatically for human consumption, prefer block style over flow style. Block style puts each key-value pair on its own line with indentation showing nesting depth, which is easier to review in code review and easier to diff in version control. Flow style, which is equivalent to JSON notation, is valid YAML but defeats the readability purpose of using YAML. Most yaml.dump implementations default to flow style for small nested objects, so explicitly setting the block style option ensures consistent output across all document sizes. Linting the output with yamllint using strict settings before committing it to a repository catches syntax issues early and enforces a consistent style for the whole team.
Quick fix checklist
- ✓Parse JSON with JSON.parse before passing to yaml.dump rather than passing the raw string
- ✓Use js-yaml with noRefs: true to avoid YAML anchor syntax in the output
- ✓Set noRefs and indent options explicitly; do not rely on js-yaml defaults
- ✓Quote any string values that match YAML 1.1 boolean keywords: yes, no, on, off
- ✓Use yaml.safe_dump in Python instead of yaml.dump to avoid !!python tags
- ✓Use the -y flag with yq when converting JSON to YAML at the command line
- ✓Validate round-trip fidelity by parsing the YAML output and comparing to the original JSON object
- ✓Run yamllint on the output before committing to a repository
Related guides
Frequently asked questions
Is JSON valid YAML?
JSON is valid YAML 1.2. The YAML 1.2 specification was explicitly designed to make JSON a strict subset. Any JSON document can be parsed by a compliant YAML 1.2 parser. However, JSON is not valid YAML 1.1, which has additional type coercions and a different grammar for some edge cases.
How do I convert JSON to YAML in Node.js?
Install js-yaml from npm, parse the JSON string with JSON.parse, then call yaml.dump on the resulting JavaScript object. Pass an options object with indent: 2 and noRefs: true for readable, portable output. Write the resulting string to a file with fs.writeFileSync using UTF-8 encoding.
How do I convert JSON to YAML on the command line?
Use the yq tool with the -y flag: yq -y . input.json > output.yaml. Without the -y flag, yq outputs JSON by default. To convert YAML back to JSON for verification, use yq -j . output.yaml and compare the result against the original JSON file.
Why does PyYAML add !!python/object tags to my YAML output?
PyYAML's default Dumper serializes arbitrary Python objects and emits type tags to enable safe reconstruction. To produce portable YAML from JSON-derived data, use yaml.safe_dump instead of yaml.dump. The safe dumper only emits standard YAML types that any compliant parser can read.
What YAML features have no JSON equivalent?
Comments, anchors, aliases, block scalar styles (literal and folded), and YAML-specific tags have no JSON equivalents. These features are lost when converting YAML to JSON. If you round-trip a hand-authored YAML file through JSON and back, you get a semantically equivalent document with all of these features stripped out.
Why does the string 'no' become false after JSON to YAML conversion?
YAML 1.1 parsers treat unquoted values like no, yes, on, and off as booleans. When js-yaml or yq converts the JSON string 'no' to YAML without quoting, and the output is then read by a YAML 1.1 parser such as Go's yaml.v2 library, it becomes the Boolean false. Quote these values explicitly to prevent silent coercion.
How do I preserve YAML anchors when editing a YAML file programmatically?
Avoid converting to JSON as an intermediate step. Use the ruamel.yaml library in Python or the yaml package (not js-yaml) in Node.js. Both provide a document model that preserves anchors, aliases, and comments when you load, modify, and re-serialize a YAML document.
What is the difference between block style and flow style in YAML?
Block style places each key-value pair on its own indented line, making the document human-readable and easy to diff in version control. Flow style uses curly braces and brackets similar to JSON notation, keeping short values on a single line. Most YAML converters produce block style by default, but you may need to set explicit options to enforce it for nested objects.
How do I validate that a JSON-to-YAML conversion preserved all data correctly?
Parse the generated YAML back into a JavaScript or Python object using the same parser your deployment target uses. Then perform a deep equality comparison against the original parsed JSON object. Check that field types match, not just values, because boolean coercion can change a string to a boolean without changing how it prints in some contexts.
All tools run in your browser. Your data never leaves your device. Last updated: 2026-05-05.