CLI tool
tidwall/gjson avatar
tidwall/gjson

tidwall/gjson: reading JSON values in Go without unmarshalling

Get JSON values quickly - JSON parser for Go

15,557 stars2,240 forksGoMIT

At a glance

What is it?
GJSON is a Go package that pulls values out of a JSON document by dot-notation path instead of decoding the whole thing into a struct. It is a good fit for hot paths and for JSON whose shape you do not control.
Who is it for?
Adopt GJSON when you read a few fields out of large or irregular JSON and want to avoid defining a struct for every payload, and when you can accept that a missing path is not an error. Do not adopt it as a validating parser or as a writer: it returns what it finds and never rejects a malformed document, and SJSON is the separate project for modification.
Can I use it commercially?
Yes. MIT 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 33 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem GJSON removes: decoding JSON you do not own

Go's encoding/json wants a destination. You define a struct, tag the fields, call Unmarshal, and get either a populated value or an error. That is fine when the payload is stable and you need most of it. It is wasteful when a response is 200 KB and you want one identifier, and it is awkward when the same endpoint returns three different shapes depending on a type field. GJSON takes the other route: it scans the raw string and returns the value at a path you name.

The README frames the package as a "fast and simple way to get values from a json document", and lists one line retrieval, dot notation paths, iteration and JSON Lines parsing as its features. The audience is Go developers who already have JSON as a string or []byte and would rather not model it. That includes log and event pipelines, API clients reading a handful of fields from a third-party response, and configuration loaders where the schema is loose.

How a path lookup actually works

gjson.Get(json, path) walks the document. A path is a series of keys separated by dots, and array elements are addressed by index. The README's own example document shows the vocabulary: "name.last" returns "Anderson", "age" returns 37, "children.#" returns the element count, "children.1" returns "Alex", and "friends.#.first" returns an array of the first names of every friend. Wildcards '?' and '*' match keys, and a literal dot inside a key is escaped with a backslash, so "fav\.movie" reaches the key named fav.movie.

Queries go further. The form #(...) returns the first element matching a condition and #(...)# returns all of them, with ==, !=, <, <=, >, >= plus the like operators % and !%. From the README's document, friends.#(last=="Murphy").first gives "Dale", friends.#(age>45)#.last gives ["Craig","Murphy"], and friends.#(nets.#(=="fb"))#.first gives ["Dale","Roger"]. Note the note about compatibility: the older #[...] bracket form still works but the README says it will be removed at the next major release, so new code should use parentheses.

The return type is gjson.Result, which carries a Type of String, Number, True, False, Null or JSON, plus fields Str, Num, Raw, Index and Indexes. Convenience methods cover the common coercions: Exists, Value, Int, Uint, Float, String, Bool, Time, Array, Map, Get, ForEach and Less. Int and Uint are documented as reading the full 64 bits, which matters because a plain float64 round trip loses precision above 2^53.

Install and first lookup

The README gives one installation command. It retrieves the library into your module cache; the module itself declares go 1.23 and depends on github.com/tidwall/match v1.1.1 and github.com/tidwall/pretty v1.2.0.

bash
go get -u github.com/tidwall/gjson

The smallest working program is the README's own. A constant holds the JSON, Get resolves the path, and String() converts the result. Running it prints Prichard.

go
package main

import "github.com/tidwall/gjson"

const json = `{"name":{"first":"Janet","last":"Prichard"},"age":47}`

func main() {
	value := gjson.Get(json, "name.last")
	println(value.String())
}

If your JSON is already a []byte, the README points to GetBytes instead, which avoids converting to a string. A realistic second step is a query over an array, where the result is itself a set of matches. The README's example returns the first names of every friend whose last name is Murphy, written as friends.#(last=="Murphy")#.first against the README's friends array.

One behaviour to internalise early: Array() on a non-existent result returns an empty array, and Array() on a value that is not a JSON array returns a one-element array containing that value. Loops written against Array() therefore do not need a nil check, but they will also not tell you that the path was wrong.

Modifiers, chaining and where the syntax gets dense

Version 1.2 added modifiers and path chaining. A modifier is a path component that transforms the document before the next component reads it, and paths are joined with the pipe character. The README's built-in list includes @reverse for arrays and object members, @ugly to strip whitespace, @pretty to reformat, and @this to return the current element. Chained, "children|@reverse" gives the children array backwards and "children|@reverse|0" gives "Jack".

This is the part of GJSON that repays reading SYNTAX.md rather than the README. The README calls itself a quick overview and defers to that file twice, once for the path syntax and once for multipaths. Multipaths are mentioned only in a footnote explaining why the query brackets changed in v1.3.0. If your paths involve several branches, escaping, or modifiers applied to query results, the README will not be enough and the syntax document is the reference.

The design cost is that path strings are strings. A typo in friends.#(last=="Murphy").first is a valid path that returns nothing; the compiler has no opinion. Struct tags have the same problem, but at least a misspelled tag usually yields a zero value you notice. Here you get a Result whose Exists() is false, and whether that is loud or silent depends entirely on whether you check.

The limitation: it reads, it does not validate or write

GJSON is a reader. It has no counterpart for setting a value; the README points to SJSON for modifying JSON and to the JJ command line tool, both separate projects. If your task is to mutate a document and write it back, GJSON is the wrong tool on its own.

The sharper limitation is validation. Nothing in the README describes schema checking, and the failure mode of a bad path is an empty Result, not an error. A service that reads a required field with GJSON and forgets Exists() will happily proceed with an empty string. Compare that with encoding/json, where a missing field leaves a zero value but a malformed document returns an error from Unmarshal. GJSON does not give you that signal. It is also not the tool for streaming a document larger than memory, since the API takes the whole JSON as a string or byte slice.

Performance is the other claim worth treating carefully. The README links to a performance section, but that section is not reproduced in the README text available here, so there is no benchmark to quote. The structural argument for speed is that a path lookup avoids allocating a full object graph; whether that holds for your payload is something you would measure yourself.

Alternatives and how they differ in approach

The default alternative is encoding/json in the standard library. It decodes into a struct or into map[string]interface{}, validates as it goes, and returns an error on malformed input. The difference is not speed, it is the contract: encoding/json gives you a typed value or an error, GJSON gives you a Result you must interrogate. If you need every field and you want errors on bad input, encoding/json is the simpler choice and needs no dependency.

The closer alternative is a general JSON path library, which also addresses values by an expression rather than by struct shape. GJSON's path language is its own, documented in SYNTAX.md, with its own operators and its own history of change, as the #[...] to #(...) migration shows. Adopting GJSON means adopting that syntax.

Within the same family, the README points to SJSON for modification and JJ for the command line, and notes that GJSON is also available for Python and Rust through separate ports. That matters if your pipeline crosses languages: the Go package is the reference implementation, and the ports are independent projects with their own release cycles.

Maintenance, licence and upgrade cost

The repository is not archived, and the last push was on 2026-08-28. The README documents two dependency versions and a go.mod that declares go 1.23, so the module tracks a recent Go toolchain rather than an old one. The dependency surface is small: tidwall/match and tidwall/pretty, both by the same author.

Upgrade cost is concentrated in two places. First, the deprecated query syntax. The README states that prior to v1.3.0 queries used #[...] brackets, that this changed in v1.3.0 to avoid confusion with multipaths, and that the old form will keep working "until the next major release". Any code written before v1.3.0 carries a migration that has not landed yet. Second, the Result API is broad, and code that reaches into the exported fields Str, Num and Raw rather than the accessor methods is coupled to the internal representation.

The licence is MIT, which the repository states in the LICENSE file. That is a permissive licence and is compatible with the usual Go module consumption, but the details of what your distribution must include are a matter for your own review, not something this article can settle.

Editorial conclusion

Adopt GJSON when you read a few fields out of large or irregular JSON and want to avoid defining a struct for every payload, and when you can accept that a missing path is not an error. Do not adopt it as a validating parser or as a writer: it returns what it finds and never rejects a malformed document, and SJSON is the separate project for modification. Before committing, verify the path syntax you need against SYNTAX.md, especially multipaths and the query operators, and confirm the module requires the Go version in go.mod.

Frequently asked questions

How do I install tidwall/gjson?

Run go get -u github.com/tidwall/gjson, which the README gives as the installation step. The module declares go 1.23 and pulls in github.com/tidwall/match v1.1.1 and github.com/tidwall/pretty v1.2.0.

What does a GJSON path look like for an array element?

Use the index as the key. In the README's example document, children.1 returns "Alex", children.# returns the element count, and friends.#.first returns an array of every friend's first name.

Does tidwall/gjson modify JSON as well as read it?

No. The README points to SJSON for modifying JSON and to the JJ command line tool, both separate projects. GJSON itself only retrieves values.

Why did GJSON queries change from #[...] to #(...)?

The README states that queries used the #[...] brackets prior to v1.3.0 and that this changed in v1.3.0 to avoid confusion with the new multipath syntax. The old form still works for backwards compatibility until the next major release.

Is there a GJSON playground?

Yes. The README's header links to a playground at tidwall.com/gjson-play alongside the API reference and the syntax document.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. tidwall/gjson on GitHub
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tidwall-gjson.svg)](https://hysenlabs.com/projects/tidwall-gjson)