yq: the roundtrip mode injects metadata into the JSON your jq filter reads
Command-line YAML, XML, TOML processor - jq wrapper for YAML/XML/TOML documents
At a glance
- What is it?
- kislyuk's yq converts YAML to JSON and hands it to jq, forwarding every argument it does not recognise, so its behaviour is mostly jq's behaviour. What makes it its own tool is a set of options that rewrite the document on the way through, and one of them will make a counting filter return four entries where there are two.
- Who is it for?
- yq is the right tool when a file is YAML and the thing you want to write is already a jq filter, because it adds one conversion step and nothing else, and it preserves mapping key order where naive converters do not. It is a bad tool to reach for when the jq filter has to be portable, because the roundtrip option changes the document jq sees and the page says so with a worked example.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 7 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 4, 2026, and from our analysis. They are not legal advice.
Editorial analysis
yq converts and forwards, so its behaviour is mostly jq's behaviour
The Synopsis is four sentences long and the last one settles the scope. yq takes YAML input, converts it to JSON, and pipes it to jq. You can also give input filenames as arguments, as in jq. By default no conversion of the jq output is done, and the --yaml-output or -y option converts it back into YAML. Then: all other command line arguments are forwarded to jq, and the jq manual is the place to read about jq features and options. The exit code follows the same rule, since yq forwards the exit code jq produced. So there is no yq filter language, no query syntax of its own and no compatibility surface of its own to learn; the cost of adopting it is one conversion and the availability of a jq binary, which the Installation section says you must install yourself.
pip install yqBecause YAML treats JSON as a dialect, the reverse direction works with no extra flag: yq -y . < in.json > out.yml.
The roundtrip option injects metadata and a count filter returns four
The --yaml-roundtrip option, spelled -Y, preserves custom YAML tags and styles by representing them as extra items in their enclosing mappings and sequences while the document is in JSON. The mechanics are stated plainly: yq carries extra metadata, in the form of mapping pairs and sequence values, in the JSON representation for any custom tags or styles it finds, and when converting back it parses that metadata, re-applies the tags and styles, and discards the extra pairs and values. The warning that follows is the most useful paragraph on the page. The -Y option is incompatible with jq filters that do not expect the extra information. A jq filter that counts entries in the Instances array will come up with 4 entries instead of 2, and a filter that expects all array entries to be mappings may break because of the presence of string metadata keys. The instruction is to check your jq filter for compatibility and semantic validity when using -Y.
Comments on a root scalar cannot round trip and the page says which
YAML comment metadata is attached to entries in mappings and sequences, which means a document whose root is a scalar has nowhere to put it. The page states the limit rather than implying it: comments on a document whose root is a string, number, boolean or null cannot currently round trip through -Y, because there is no enclosing collection to carry their metadata through jq. The example given is a Markdown heading after a YAML document separator, which is treated as a comment on a root string and is lost. There is a second mechanism for that case: the --yaml-frontmatter or -F option, which preserves the entire body, and the frontmatter section then carries its own restrictions. Only the first document is sent to jq, an initial triple dash opens the header and an unindented triple dash or document end marker closes it, and the filter must produce exactly one document.
The exit code comes from jq unless YAML parsing failed
One sentence in the Synopsis is the whole of the scripting contract: yq forwards the exit code jq produced, unless there was an error in YAML parsing, in which case the exit code is 1. So a yq invocation can fail in two distinguishable ways, a jq filter that evaluates to false or errors, which inherits jq's status, and a document jq never sees because the YAML would not parse, which is normalised to 1. There is no documented third mode for a yq-level failure such as a malformed frontmatter block. Two more options in the same paragraph round out the output control: --width or -w passes the line wrap width for string literals with 0 disabling wrapping, and --explicit-start and --explicit-end emit YAML markers even for a single document. YAML output also preserves an explicit leading document marker from the input and emits one for multidocument streams.
The package installs three commands and the packaging names two formats
One Python package produces three executables. The project scripts table maps yq to the cli entry point, xq to xq_cli for the XML path, and tomlq to tq_cli, and the TOML path is the reason tomlkit is in the dependency list. So the repository description, which calls the project a command line YAML, XML and TOML processor, is accurate about capability and the packaging metadata is not, because the pyproject description reads command line YAML/XML processor, a jq wrapper for YAML/XML documents, with TOML absent from both the description and the topic classifiers. The Homebrew line in the same section uses a third naming convention again: on macOS, yq is also available on Homebrew, to be installed with brew install python-yq. So the pip package is named yq, the Homebrew formula is named python-yq, and the XML tool is named xq.
make lint finds its target by globbing and mypy installs stubs as it runs
The build file locates its own source directory by shell globbing rather than by configuration. Both halves of the lint target take the same argument, the directory name of the first match for */__init__.py, and pass it to the tools:
lint:
ruff check $$(dirname */__init__.py)
mypy --install-types --non-interactive $$(dirname */__init__.py)Two things follow. Anything outside a directory containing an __init__.py is not linted or type checked, which in this tree means the docs directory, the test directory and any loose script at the root are outside the gate. And mypy is invoked with --install-types and --non-interactive, so a missing stub package is fetched and installed into the environment rather than reported. The test target is a single script run directly, python ./test/test.py -v, rather than a test runner, and the tree holds test/ as a directory to match.
make install pulls the test extra and make docs pip installs as it builds
Two targets have side effects beyond their names. The install target removes dist, installs the build frontend into the current environment, builds a wheel, and then installs that wheel with the test extra appended to its filename, so make install pulls ruff, coverage, build, wheel and mypy into whatever environment you ran it in. The docs target installs furo and sphinx-copybutton with pip before invoking the Sphinx build, and it writes the HTML output into docs/html, which is inside the source directory it is building from. There is a separate init_docs target that runs sphinx-quickstart, which is the interactive scaffolder, so a fresh checkout has two ways to get a docs tree. The phony list declares test, lint, release and docs, and release has no recipe in this file, so it is presumably supplied by the common.mk the Makefile includes at the end.
A module entry point makes in-place editing the documented path
Beyond the three installed commands there is a fourth way in, and the readme points at it with a concrete example. yq can be called as a module if needed, and with -y or -Y files can be edited in place like with sed:
python -m yq -Y --indentless --in-place '.["current-context"] = "staging-cluster"' ~/.kube/configThe example is a kubeconfig rewrite, which is the clearest statement of the intended use: point a jq expression at a real configuration file and change one field. The frontmatter option has the same shape for Markdown files, yq -Y --yaml-frontmatter '.draft = false' post.md, and yq -iYF with several files at once. One naming detail is called out explicitly because it looks like it should not be: the -f option still means jq's --from-file. The frontmatter flag is -F, uppercase, and the page makes sure the two are not confused.
Editorial conclusion
yq is the right tool when a file is YAML and the thing you want to write is already a jq filter, because it adds one conversion step and nothing else, and it preserves mapping key order where naive converters do not. It is a bad tool to reach for when the jq filter has to be portable, because the roundtrip option changes the document jq sees and the page says so with a worked example. Before using -Y, count how many filters will run against the result and check each one for assumptions about array length or entry shape. And if you are validating rather than transforming, note that the page documents no validation mode: the only failure signal it names is a YAML parse error producing exit code 1.
Frequently asked questions
What does the yq command do?
It takes YAML input, converts it to JSON, and pipes it to jq. Input filenames can be given as arguments, no conversion of the jq output happens by default, and the --yaml-output or -y option converts it back to YAML. All other command line arguments are forwarded to jq.
What is the difference between jq and yq?
yq is the wrapper: it converts YAML to JSON and hands it to jq, which does all the actual filtering. The page describes no query language of its own and no comparison with any other tool also named yq; the options yq adds are the output format flags, the line width, the explicit document markers and the roundtrip and frontmatter modes.
How do I install yq?
Run pip install yq, and install jq separately, since yq pipes to it. On macOS there is also a Homebrew formula, and the readme names it brew install python-yq rather than yq. The page gives no distribution specific instructions beyond those two.
What does yq -Y do to my jq filters?
The -Y or --yaml-roundtrip option preserves custom tags and styles by carrying extra metadata as mapping pairs and sequence values in the JSON. The page warns that it is incompatible with filters that do not expect it: a filter counting entries in the Instances array comes up with 4 instead of 2, and a filter expecting all array entries to be mappings may break because of string metadata keys.
What exit code does yq return?
yq forwards the exit code jq produced. The one exception is an error in YAML parsing, in which case the exit code is 1. The page documents no separate status for a yq level failure.
Which commands does the yq package install?
Three. yq for YAML, xq for XML which transcodes to JSON with xmltodict before piping to jq, and tomlq for TOML. The package can also be run as a module with python -m yq, which is how the readme shows editing a file in place.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/kislyuk-yq)