# gojq: a pure Go jq you can embed in your own binary

> gojq reimplements the jq command in Go, with arbitrary-precision integers and a library API. The trade-offs are object key order and a handful of jq flags it will not implement.

**itchyny/gojq** — Pure Go implementation of jq

- Repository: https://github.com/itchyny/gojq
- Stars: 3,807 · Forks: 156
- Language: Go
- License: MIT
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/itchyny-gojq

## What gojq solves, and the jq problem it inherits

jq is the standard way to slice JSON on a command line, but the reference implementation is written in C. That matters when you want to ship a single static binary, cross-compile without a C toolchain, or call jq filters from inside a Go program. gojq is an implementation of the jq command written in Go, and the README states you can also embed it as a library in your Go products. The audience is therefore two groups: engineers who want jq syntax in environments where building C is awkward, and Go developers who want jq's query language available in-process instead of shelling out to an external binary.

The project does not try to be a drop-in replacement in every respect. The README has a dedicated section on differences from jq, and it is unusually candid about the ones it considers deliberate. That honesty is useful: you can read the list and decide in a few minutes whether your filters survive the move.

## How the interpreter is put together

The repository layout shows a conventional hand-written Go pipeline rather than a wrapper around libjq. There is a lexer (lexer.go), a yacc grammar (parser.go.y, compiled to parser.go), a compiler (compiler.go), and an interpreter (execute.go). Built-in functions live in builtin.jq, which the Makefile turns into builtin.go through go generate. Environment and variable handling sit in env.go and scope_stack.go; value comparison in compare.go.

The library entry point is small. gojq.Parse returns a *Query, and Query.Run takes an input value and returns an iterator. That iterator model is the interesting design choice: a jq filter can produce zero, one, or many outputs, so the Go API hands you values one at a time rather than a slice. Errors travel through the same channel as values, which is why the README's example type-asserts each result to error before printing it.

Dependencies are light: go-cmp for tests, go-isatty and go-runewidth for terminal behavior, timefmt-go for time formatting, and an itchyny YAML library. Nothing links against libjq.

## Installing gojq and running a first filter

The README lists four installation routes. Homebrew and mise cover macOS and Linux workstations; Zero Install fetches a packaged build; go install builds from source and requires a Go toolchain. Docker images are published on both Docker Hub and GitHub Container Registry.

```bash
brew install gojq
```

If you prefer to build from source, the module path is the same one the library uses.

```bash
go install github.com/itchyny/gojq/cmd/gojq@latest
```

A first real use is the same shape as jq: pipe JSON in, give a filter as an argument. The README's example reads a field and prints the number.

```bash
echo '{"foo": 128}' | gojq '.foo'
```

You should see 128 on stdout. Filters chain with the pipe operator, and assignment works too.

```bash
echo '{"a":1,"b":2}' | gojq '.a += 1 | .b *= 2'
```

That prints an object with a set to 2 and b set to 4. If you want to try the arithmetic claim without any input file, the -n flag runs the filter with null as input.

```bash
gojq -n 'def fact($n): if $n < 1 then 1 else $n * fact($n - 1) end; fact(50)'
```

The README shows this producing the full 65-digit value of 50 factorial, which is the arbitrary-precision integer behaviour in action.

## Arbitrary-precision integers, and where that guarantee stops

The README states that gojq supports arbitrary-precision integer calculation while jq does not, and that jq loses precision on large integers when calculation is involved. The example input 4722366482869645213696 round-trips through gojq unchanged. That is a real difference for anyone handling identifiers, timestamps in nanoseconds, or currency amounts stored as integers.

The guarantee is narrower than the headline suggests, and the README says so. All mathematical functions, including floor and round, convert integers to floating-point numbers. Only addition, subtraction, multiplication, modulo, and division when the division is exact keep integer precision. The README offers two workarounds: a def idivide($n) helper for floor division, and a def ifloor helper for rounding down floats, with the warning that ifloor does not work with large floating-point numbers and loses precision on large integers. If your pipeline runs arithmetic through floor or round, gojq will not save you.

## Key order, missing flags, and the parts of jq gojq refuses

The most consequential limitation is stated plainly: gojq does not keep the order of object keys, because it uses map[string]any internally. The README acknowledges this may cause problems and argues that scripts should not rely on key order. Because of it, gojq has no keys_unsorted function and no --sort-keys (-S) option, and the author notes he would implement ordering if Go's standard library gained an ordered map, adding that he is less motivated to do so otherwise.

Other omissions are deliberate. Unsupported functions include get_jq_origin, get_prog_origin, get_search_list, input_line_number, and $__loc__, the last two for performance reasons. Unsupported flags include --ascii-output (-a) and --seq, both for performance or commonality reasons, plus --unbuffered, since gojq is unbuffered by default. gojq does not parse the JSON extensions NaN, Infinity, or [000]. It does not support BOM because encoding/json does not. It does not support some regular expression metacharacters, backreferences, look-around assertions, or some flags, a consequence of using Go's regexp engine rather than PCRE. Keywords cannot be used as function names, so def true: .; true is rejected, and module name prefixes in function declarations are undocumented.

Some of these are corrections rather than gaps. The README lists behaviours where gojq deviates from jq on purpose, expecting jq to change: string indexing such as "abcde"[2], handling files with no trailing newline, @base64d accepting a binary string, and several time-formatting fixes including %f in strftime and strptime, timezone offsets in fromdate and fromdateiso8601, %Z and %z in strptime, and nanosecond support. If you are porting a filter that touches dates, gojq may be more correct than the jq you have installed.

## Using gojq as a Go library instead of a binary

The README's library example imports github.com/itchyny/gojq, calls gojq.Parse on a filter string, and then loops over query.Run(input). The loop checks for a *gojq.HaltError with a nil value to stop cleanly, and treats any other non-nil error as fatal. That pattern matters because jq's halt and halt_error semantics are part of the language, and the iterator surfaces them as values rather than panicking.

The practical consequence is that you can ship one binary that evaluates user-supplied jq filters without a subprocess, a shell, or a C dependency. RunWithContext exists as an alternative to Run when you need cancellation. The README points to gojq.ParseError for inspecting syntax failures, which is what you would surface to a user who typed a bad filter. Note that the module path for the library and the command are the same: github.com/itchyny/gojq, with the CLI under cmd/gojq.

YAML is a gojq extension worth knowing about. The README states gojq supports reading with --yaml-input and writing with --yaml-output, which jq does not. For configuration pipelines that start as YAML, that removes a conversion step.

## Colour output, environment variables and licence

Colour is handled automatically: gojq disables it when stdout is not a tty, when NO_COLOR is set to a non-empty value, or when TERM is dumb. The --color-output (-C) flag forces colour anyway, and --monochrome-output (-M) disables it and takes precedence. Individual colours come from the GOJQ_COLORS environment variable, a colon-separated list of ANSI escape sequences covering null, false, true, numbers, strings, object keys, arrays, and objects. The documented default is 90:33:33:36:32:34;1. If your CI logs look wrong, that variable is the first thing to check.

The project is MIT licensed, which is permissive and compatible with embedding in closed-source Go programs. That is a practical advantage over jq for anyone shipping a product, though it is not legal advice and you should confirm obligations with your own counsel. Maintenance is ongoing: the last push to main was on 2026-09-23, and the most recent tagged release is v0.12.19 from 2026-04-01. The repository is not archived. Upgrading means bumping a module version or a package manager formula; there is no migration tooling described, and the README does not document a rollback procedure, so pin a version if you depend on a specific behaviour.

## Conclusion

Adopt gojq when you want jq syntax without a C toolchain, when you need arbitrary-precision integer arithmetic, or when you want to run jq filters inside a Go program through the library API. Do not adopt it if your scripts depend on object key order, on keys_unsorted, on --sort-keys, or on PCRE features such as backreferences and look-around assertions, because the README states those are unsupported. Before committing, check your existing filters against the list of unsupported functions and flags, and confirm that the missing --ascii-output and --seq options are not in your pipelines.

## FAQ

### What is gojq and how does it differ from jq?

gojq is an implementation of the jq command written in Go, which can also be embedded as a library in Go products. The README lists the main differences: it does not keep object key order, it supports arbitrary-precision integer arithmetic, it reads and writes YAML, and it omits some jq functions, flags and regex features.

### How do I install gojq?

The README gives four routes: brew install gojq, mise use -g gojq@latest, 0install add gojq https://apps.0install.net/utils/gojq.xml, or go install github.com/itchyny/gojq/cmd/gojq@latest. It also publishes Docker images at itchyny/gojq and ghcr.io/itchyny/gojq.

### How do I use gojq to filter JSON?

Pipe JSON into it and pass a filter as the argument, for example echo '{"foo": 128}' | gojq '.foo', which prints 128. Filters compose with the pipe operator, so echo '{"a":1,"b":2}' | gojq '.a += 1 | .b *= 2' produces an object with a as 2 and b as 4.

### Can I use gojq inside a Go program?

Yes. The README shows importing github.com/itchyny/gojq, calling gojq.Parse on a filter string, and iterating over query.Run(input), checking each returned value for an error before using it. RunWithContext is available when you need a context.

### Why does gojq not have keys_unsorted or --sort-keys?

Because gojq does not keep the order of object keys, since it uses map[string]any internally. The README states the author would implement ordering if Go's standard library gained an ordered map, but that he is less motivated to do so now.

## Sources

- [Issues](https://github.com/itchyny/gojq/issues)
- [itchyny/gojq on GitHub](https://github.com/itchyny/gojq)
- [License: MIT](https://github.com/itchyny/gojq/blob/main/LICENSE)
- [README](https://github.com/itchyny/gojq/blob/main/README.md)
- [Releases](https://github.com/itchyny/gojq/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/itchyny-gojq
