jq++: JSON with structural reuse and expression evaluation
jq++ is a tool that extends your JSON with structural reuse: structural composition (via $extends and $includes) and expression evaluation (via eval:).
It productizes the ideas of jq-front, which was named after Cfront[3]—the classic C++-to-C translator—and gives JSON a similar “front-end” power.
Despite criticism of JSON[6] as a format for configuration[4], JSON has clear strengths: broad tool support, a good balance between machine and human readability, and simple, unambiguous syntax.
Where JSON falls short is in structure. When you need many similar but slightly different configurations, redundancy is hard to remove because JSON does not define relationships between files or between nodes inside a file.[1]
What is it good for?
Have you ever had to maintain configuration files that are almost the same, but not quite?
| config for foo | config for bar |
|---|---|
|
|
With jq++, you can define a single base JSON for the common parts:
{
"server": {
"ip": "192.168.1.5",
"port": 2001
},
"user": {
"name": "root"
}
}
Then reuse it from other files via $extends:
| foo.json | bar.json |
|---|---|
|
|
Run jq foo.json` and you get the expanded “config for foo” from the first table; `jq bar.json gives you the “config for bar”.
You keep one source of truth and generate each variant by extending it.
Inheritance merge semantics
When jq++ resolves $extends or $includes, it uses index-wise recursive merge:
-
object values are merged recursively
-
array values are merged by index
-
if both sides have an element at the same index, those elements are merged recursively
-
if only one side has an element at an index, that element is kept
This is especially useful when you want to override only part of an array element without rewriting the entire array.
For example, suppose a parent defines:
{
"spec": {
"http": [
{
"match": [
{
"headers": {
"end-user": {
"exact": "jason"
}
}
}
],
"fault": {},
"route": [
{
"destination": "v1"
}
]
},
{
"route": [
{
"destination": "v2"
}
]
}
]
}
}
and the child defines:
{
"$extends": ["parent.json"],
"spec": {
"http": [
{
"fault": {
"abort": {
"httpStatus": 500
}
}
}
]
}
}
Running jq++ child.json produces:
{
"spec": {
"http": [
{
"match": [
{
"headers": {
"end-user": {
"exact": "jason"
}
}
}
],
"fault": {
"abort": {
"httpStatus": 500
}
},
"route": [
{
"destination": "v1"
}
]
},
{
"route": [
{
"destination": "v2"
}
]
}
]
}
}
The child’s spec.http[0].fault overrides only that part of the first element.
The parent’s match, route, and second array element are preserved.
Expression evaluation
jq++ also supports expression evaluation so you can compute values dynamically using jq expressions in string fields.
Example: extend two files and build a greeting string from their fields and the current date:
{
"$extends": ["greeting.json", "name.json"],
"sayHello": "eval:refexpr(\".greeting\") + \", \" + refexpr(\".yourname\") + \". Today is \" + (now|todate)"
}
With greeting.json as {"greeting":"Hello"} and name.json as {"yourname":"Mark"}, running jq++ sayHello.json produces:
{
"greeting": "Hello",
"sayHello": "Hello, Mark. Today is 2026-02-14T13:07:57Z",
"yourname": "Mark"
}
YAML?
JSON is often criticized as a configuration language[4]. Whether or not you agree, jq++ can still improve a YAML-based workflow: you keep YAML for editing, then convert to JSON, run jq++ for structural reuse, and optionally convert back to YAML.
| INPUT | OUTPUT |
|---|---|
|
|
Example pipeline (using yq for YAML↔JSON):[8]
$ yq . -j in.yaml | jq++ | yq . -y
HOCON?
HOCON[hocon] is another human-friendly format with inheritance and references. You might still prefer jq++:
-
Standard JSON in and out. jq++ reads and writes JSON, which fits into almost any toolchain.
-
Custom behavior via expression evaluation. You can define how values are computed using
eval:expressions without leaving JSON. -
Separation of concerns. Data structure (inheritance, references) and human-friendly syntax are different problems; jq++ focuses on structure so you can choose the syntax (JSON, YAML, etc.) separately.
That separation matters in automation and CI/CD. Configs often need to be generated by programs; JSON is easy to generate and parse. HOCON, as a superset of JSON, is less straightforward to emit: a program might produce valid HOCON that looks different from hand-written style after a round trip. If you use jq++ for the “structure and value resolution” step and keep JSON (or YAML) as the interchange format, you avoid mixing style and semantics in one layer.
References
-
[1] jq (jqplusplus) on GitHub. https://github.com/dakusui/jqplusplus[jq]: 2025
-
[2] jq-front (original concept). jq-front: 2019
-
[3] Cfront. Wikipedia: 2019
-
[4] Thayne McCombs. Why JSON isn’t a Good Configuration Language: 2018
-
[5] YAML. Wikipedia: 2019
-
[6] JSON. json.org: 2019
-
[7] jq manual. jq: 2019
-
[8] More complex inheritance in YAML? Stack Overflow: 2019
-
HOCON. HOCON: 2020