jq

package module
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 19, 2026 License: MIT Imports: 8 Imported by: 1

README

build coverage Docs

jq

Go JSON structure query path getter/setter

package main

import (
	"encoding/json"
	"fmt"

	"github.com/linkdata/jq"
)

const rawJson = `{
  "name": "John Doe",
  "age": 30,
  "isStudent": false,
  "hobbies": ["reading", "hiking", "gaming"],
  "address": {
    "street": "123 Main St",
    "city": "Anytown",
    "zip": "12345"
  }
}`

type Address struct {
	Street string `json:"street"`
	City   string `json:"city"`
	Zip    string `json:"zip"`
}

type Person struct {
	Name      string   `json:"name"`
	Age       int      `json:"age"`
	IsStudent bool     `json:"isStudent"`
	Hobbies   []string `json:"hobbies"`
	Address   Address  `json:"address"`
}

func main() {
	var person Person
	var err error
	if err = json.Unmarshal([]byte(rawJson), &person); err == nil {
		var firsthobby string
		if firsthobby, err = jq.GetAs[string](&person, "hobbies.0"); err == nil {
			fmt.Println(firsthobby)
			var address Address
			if address, err = jq.GetAs[Address](&person, "address"); err == nil {
				fmt.Println(address.City)
			}
		}
	}
	if err != nil {
		panic(err)
	}
	// Output:
	// reading
	// Anytown
}

Struct fields

Struct path components exactly match names selected by encoding/json's default field-selection rules, including JSON tag names and unambiguous promoted fields. An untagged anonymous struct field contributes its promoted fields directly to the containing struct's namespace; it does not add its Go type name as a path component. For example, an anonymous Inner exposes value. Tagging the field json:"inner" replaces that path with inner.value; json:"Inner" uses Inner.value.

Reachable exported fields promoted through unexported embedded structs are readable with Get and writable with Set when addressable, including through map-to-struct assignments.

An exact json:"-" tag excludes a field from path traversal and map-to-struct assignments.

When an unexported embedded field has an explicit JSON name, Get and Set can traverse that component on paths to reachable exported fields. A path ending at the embedded field returns ErrPathNotFound, as does a map-to-struct assignment to it.

Assignments

Set converts among integer kinds other than uintptr and floating-point kinds using Go's numeric conversion rules. These conversions can truncate, wrap, or lose precision and do not report overflow.

For map inputs assigned to structs, only entries with matching string keys update fields; all other entries are ignored.

A nil interface value supplied by the map stores the field's zero value. A pointer supplied for a pointer or interface field is not dereferenced and must be assignable to that field; an interface field retains the pointer's dynamic type. For other fields, Set dereferences a non-nil pointer and stores the field's zero value for a nil pointer only when its pointed-to type is assignable or supported by Set's numeric-conversion or map-to-struct rules; otherwise Set returns ErrTypeMismatch.

For an existing struct, unselected fields are retained and Set reports no write if no selected field changes; an appended struct starts from zero. Existing overlays are shallow: preserved pointers retain identity, and successful updates to promoted fields reached through embedded pointers are visible through other aliases.

When an interface contains a struct value, Set cannot replace values stored inline in that struct, including nested struct fields and array elements. It can update pointees, existing map entries, and existing slice elements reachable from the struct. Attempts to replace an unaddressable field or array element, or to grow a slice with an unaddressable header, return ErrPathNotFound.

Change detection

Set reports whether it performed a write. For an existing destination value, Set skips an assignable replacement when reflect.DeepEqual reports equality. This is Go deep equality, not equality of serialized JSON; Set does not marshal values to make this decision.

Consequently, Set can report no write and retain an existing pointer, map, or slice, including its aliasing, when a distinct replacement is deeply equal. The replacement is not installed. When their referenced data is independent, later mutations through the replacement are not visible through the retained value.

Checked updates

SetChecked tentatively applies the same operation as Set, then calls a checker against the resulting object. A checker error restores the original object and is returned unchanged. A checker panic also restores the object before the panic continues.

The checker runs only when Set reports a write. A deeply equal assignable replacement skipped by Set therefore does not invoke it. The checker may inspect or marshal the tentative object, but it must not mutate it. When the checker uses Get, a path ending at an explicitly named unexported embedded field returns ErrPathNotFound, even after a tentative update beneath it. Callers are responsible for synchronizing access throughout SetChecked, including while the checker runs.

Documentation

Overview

Example
package main

import (
	"encoding/json"
	"fmt"

	"github.com/linkdata/jq"
)

const rawJson = `{
  "name": "John Doe",
  "age": 30,
  "isStudent": false,
  "hobbies": ["reading", "hiking", "gaming"],
  "address": {
    "street": "123 Main St",
    "city": "Anytown",
    "zip": "12345"
  }
}`

type Address struct {
	Street string `json:"street"`
	City   string `json:"city"`
	Zip    string `json:"zip"`
}

type Person struct {
	Name      string   `json:"name"`
	Age       int      `json:"age"`
	IsStudent bool     `json:"isStudent"`
	Hobbies   []string `json:"hobbies"`
	Address   Address  `json:"address"`
}

func main() {
	var person Person
	var err error
	if err = json.Unmarshal([]byte(rawJson), &person); err == nil {
		var firsthobby string
		if firsthobby, err = jq.GetAs[string](&person, "hobbies.0"); err == nil {
			fmt.Println(firsthobby)
			var address Address
			if address, err = jq.GetAs[Address](&person, "address"); err == nil {
				fmt.Println(address.City)
			}
		}
	}
	if err != nil {
		panic(err)
	}
}
Output:
reading
Anytown

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrInvalidReceiver = errors.New("jq: invalid receiver")

ErrInvalidReceiver indicates that Set or SetChecked received a nil or non-pointer receiver.

View Source
var ErrPathNotFound errPathNotFound

ErrPathNotFound is returned when a JSON path cannot be resolved.

View Source
var ErrTypeMismatch errTypeMismatch

ErrTypeMismatch is returned when a value does not have the expected type.

Functions

func Get

func Get(obj any, jspath string) (val any, err error)

Get returns the value at jspath in obj.

An empty path returns obj itself unless obj is nil. Values containing maps, slices, or pointers may share their backing data with obj.

Struct components exactly match names selected by encoding/json's default struct-field rules, including JSON tag names and unambiguous promoted fields. An untagged anonymous struct field contributes its promoted fields directly to the containing struct's namespace; it does not add its Go type name as a path component. A valid explicit JSON name on the field replaces that promotion with a component named by the tag.

Exported fields promoted through unexported embedded structs are readable with Get and writable with Set when addressable, including through map-to-struct assignments. An exact `json:"-"` tag excludes a field from the path namespace. Get and Set can traverse an explicitly named unexported embedded field on paths to reachable exported fields, but a path ending at the embedded field returns an error matching ErrPathNotFound. Traversing a nil pointer or an unresolved pointer/interface cycle returns the same error.

When traversal reaches an array or slice, a component is a valid index only if it is "0" or begins with an ASCII digit from '1' through '9' followed by zero or more ASCII decimal digits. The index must be at most 4294967294 and representable as int; otherwise the error matches ErrPathNotFound.

func GetAs

func GetAs[T any](obj any, jspath string) (val T, err error)

GetAs returns the value at jspath in obj as T.

It returns ErrTypeMismatch when the resolved value is not assignable to T.

func Set

func Set(obj any, jspath string, val any) (changed bool, err error)

Set updates jspath in obj and reports whether it performed a write.

obj must be a non-nil pointer. An empty path targets the pointed-to value. Array and slice components follow the index syntax documented by Get. A path into a settable slice may append one element by using an index equal to the slice's current length. Map paths address existing string-keyed entries and do not create new entries. A nil val stores the destination type's zero value.

Set converts among integer kinds other than uintptr and floating-point kinds using Go numeric conversion rules. These conversions can truncate, wrap, or lose precision and do not report overflow.

Struct components and string keys in map-to-struct assignments follow Get's field-selection rules. Only entries with matching string keys update fields; all other entries are ignored.

A nil interface value supplied by the map stores the field's zero value. A pointer supplied for a pointer or interface field is not dereferenced and must be assignable to that field; an interface field retains the pointer's dynamic type. For other fields, Set dereferences a non-nil pointer and stores the field's zero value for a nil pointer only when its pointed-to type is assignable or supported by Set's numeric-conversion or map-to-struct rules; otherwise Set returns an error matching ErrTypeMismatch.

For an existing struct, unselected fields are retained and Set reports no write if no selected field changes; an appended struct starts from zero. Existing overlays are shallow: preserved pointers retain identity, and successful updates to promoted fields reached through embedded pointers are visible through other aliases.

When an interface contains a struct value, Set cannot replace values stored inline in that struct, including nested struct fields and array elements. It can update pointees, existing map entries, and existing slice elements reachable from the struct. Attempts to replace an unaddressable field or array element, or to grow a slice with an unaddressable header, return an error matching ErrPathNotFound.

Set can traverse an explicitly named unexported embedded field to update a reachable exported field. A path ending at the embedded field, or a map-to-struct key selecting it, returns an error matching ErrPathNotFound. Set does not allocate nil pointers. Traversing a nil pointer or an unresolved pointer/interface cycle returns the same error.

For an existing destination value, Set skips an assignable replacement when reflect.DeepEqual reports that the current and replacement values are equal. This is Go deep equality, not equality of serialized JSON; Set does not marshal values to make this decision. Set can therefore report no write and retain an existing pointer, map, or slice, including its aliasing, when a distinct replacement is deeply equal.

Set leaves obj unchanged when it returns an error. It does not synchronize access to obj; callers must prevent concurrent reads and writes.

func SetChecked added in v0.2.0

func SetChecked(obj any, jspath string, val any, check func() error) (changed bool, err error)

SetChecked updates jspath only when check accepts the tentative result.

It performs the same update as Set, including updates to reachable exported fields through explicitly named unexported embedded fields. If the update reports a write, SetChecked calls check exactly once while obj contains the tentative result. A nil error commits the update. If check returns an error, SetChecked restores obj, returns false, and returns the error unchanged. If check panics, SetChecked restores obj before the panic continues. A nil check behaves like Set.

check is not called if the update is invalid or reports no write, including when Set skips a deeply equal assignable replacement. When check uses Get to inspect obj, a path ending at an explicitly named unexported embedded field returns an error matching ErrPathNotFound, even after a tentative update beneath it; longer paths to reachable exported fields remain readable.

check must not mutate obj or values reachable from it, call Set or SetChecked on them, or retain references into a rejected value. Rollback restores only changes made by SetChecked.

SetChecked does not synchronize access to obj. Callers must prevent concurrent access for the entire call, including while check runs.

Example
package main

import (
	"encoding/json"
	"errors"
	"fmt"

	"github.com/linkdata/jq"
)

func main() {
	value := []string{"one"}
	maxBytes := len(`["one"]`)

	_, err := jq.SetChecked(&value, "1", "two", func() (err error) {
		var data []byte
		if data, err = json.Marshal(value); err == nil && len(data) > maxBytes {
			err = errors.New("value is too large")
		}
		return
	})

	fmt.Println(value)
	fmt.Println(err)
}
Output:
[one]
value is too large

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL