README
gojq
Pure Go implementation of jq
This is an implementation of jq command written in Go language. You can also embed gojq as a library to your Go products.
Usage
$ echo '{"foo": 128}' | gojq '.foo'
128
$ echo '{"a": {"b": 42}}' | gojq '.a.b'
42
$ echo '{"id": "sample", "10": {"b": 42}}' | gojq '{(.id): .["10"].b}'
{
"sample": 42
}
$ echo '[{"id":1},{"id":2},{"id":3}]' | gojq '.[] | .id'
1
2
3
$ echo '{"a":1,"b":2}' | gojq '.a += 1 | .b *= 2'
{
"a": 2,
"b": 4
}
$ echo '{"a":1} [2] 3' | gojq '. as {$a} ?// [$a] ?// $a | $a'
1
2
3
$ echo '{"foo": 4722366482869645213696}' | gojq .foo
4722366482869645213696 # keeps the precision of number while jq does not
$ gojq -n 'def fact($n): if $n < 1 then 1 else $n * fact($n - 1) end; fact(50)'
30414093201713378043612608166064768844377641568960512000000000000 # arbitrary-precision integer calculation
Nice error messages.
$ echo '[1,2,3]' | gojq '.foo & .bar'
gojq: invalid query: .foo & .bar
.foo & .bar
^ unexpected token "&"
$ echo '{"foo": { bar: [] } }' | gojq '.'
gojq: invalid json: <stdin>
{"foo": { bar: [] } }
^ invalid character 'b' looking for beginning of object key string
Installation
Homebrew
brew install gojq
Build from source
go get github.com/itchyny/gojq/cmd/gojq
Docker
docker run -i --rm itchyny/gojq
Difference to jq
- gojq is purely implemented with Go language and is completely portable. jq depends on the C standard library so the availability of math functions depends on the library. jq also depends on the regular expression library and it makes build scripts complex.
- gojq implements nice error messages for invalid query and JSON input. The error message of jq is sometimes difficult to tell where to fix the query.
- gojq does not keep the order of object keys. I understand this might cause problems for some scripts but basically we should not rely on the order of object keys. I would implement when ordered map is implemented in the standard library of Go but I'm less motivated.
- gojq supports arbitrary-precision integer calculation while jq does not. This is important to keeping the precision of numeric IDs or nanosecond values. You can use gojq to solve some mathematical problems which require big integers.
- gojq supports reading from YAML input while jq does not. gojq also supports YAML output.
Usage as a library
You can use the gojq parser and interpreter from your Go products.
package main
import (
"fmt"
"log"
"github.com/itchyny/gojq"
)
func main() {
query, err := gojq.Parse(".foo | ..")
if err != nil {
log.Fatalln(err)
}
input := map[string]interface{}{"foo": []interface{}{1, 2, 3}}
iter := query.Run(input) // or query.RunWithContext
for {
v, ok := iter.Next()
if !ok {
break
}
if err, ok := v.(error); ok {
log.Fatalln(err)
}
fmt.Printf("%#v\n", v)
}
}
- Firstly, use
gojq.Parse(string) (*Query, error)
to get the query from a string. - Secondly, get the result iterator
- using
query.Run
orquery.RunWithContext
- or alternatively, compile the query using
gojq.Compile
and thencode.Run
orcode.RunWithContext
. You can reuse the*Code
against multiple inputs to avoid compiling the same query. - In either case, the query input should have type
[]interface{}
for an array andmap[string]interface{}
for a map (just like decoded to aninterface{}
using the encoding/json package). You can't use[]int
ormap[string]string
, for example.
- using
- Thirdly, iterate through the results using
iter.Next() (interface{}, bool)
. The iterater can emit an error so make sure to handle it. Termination is notified by the second returned value ofNext()
. The reason why the return type is not(interface{}, error)
is that the iterator can emit multiple errors and you can continue after an error.
gojq.Compile
allows to configure the following compiler options.
gojq.WithModuleLoader
allows to load modules. By default, the module feature is disabled. If you want to load modules from the filesystem, usegojq.NewModuleLoader
.gojq.WithEnvironLoader
allows to configure the environment variables referenced byenv
and$ENV
. By default, OS environment variables are not accessible due to security reason. You can usegojq.WithEnvironLoader(os.Environ)
if you want.gojq.WithVariables
allows to configure the variables which can be used in the query. Pass the values of the variables tocode.Run
in the same order.gojq.WithFunction
allows to add a custom internal function.gojq.WithInputIter
allows to useinput
andinputs
functions. By default, these functions are disabled.
Bug Tracker
Report bug at Issues・itchyny/gojq - GitHub.
Author
itchyny (https://github.com/itchyny)
License
This software is released under the MIT License, see LICENSE.
Documentation
Overview ¶
Package gojq provides the parser and interpreter of gojq.
Please refer to https://github.com/itchyny/gojq#usage-as-a-library for introduction of the usage as a library.
Index ¶
- func Marshal(v interface{}) ([]byte, error)
- type Array
- type Bind
- type Code
- type CompilerOption
- func WithEnvironLoader(environLoader func() []string) CompilerOption
- func WithFunction(name string, minarity int, maxarity int, ...) CompilerOption
- func WithInputIter(inputIter Iter) CompilerOption
- func WithModuleLoader(moduleLoader ModuleLoader) CompilerOption
- func WithVariables(variables []string) CompilerOption
- type ConstArray
- type ConstObject
- type ConstObjectKeyVal
- type ConstTerm
- type Foreach
- type Func
- type FuncDef
- type If
- type IfElif
- type Import
- type Index
- type Iter
- type Label
- type ModuleLoader
- type Object
- type ObjectKeyVal
- type ObjectVal
- type Operator
- type Pattern
- type PatternObject
- type Query
- type Reduce
- type String
- type Suffix
- type Term
- type TermType
- type Try
- type Unary
Examples ¶
Constants ¶
Variables ¶
Functions ¶
func Marshal ¶
Marshal returns the jq-flavored JSON encoding of v.
This method only accepts limited types (nil, bool, int, float64, *big.Int, string, []interface{} and map[string]interface{}) because these are the possible types a gojq iterator can emit. This method marshals NaN to null, truncates infinities to (+|-) math.MaxFloat64 and does not escape '<' and '>' for embedding in HTML. These behaviors are based on the marshaler of jq command and different from the standard library method json.Marshal.
Types ¶
type Code ¶
type Code struct {
// contains filtered or unexported fields
}
Code is a compiled jq query.
func Compile ¶
func Compile(q *Query, options ...CompilerOption) (*Code, error)
Compile compiles a query.
Example ¶
Code:
package main import ( "fmt" "github.com/itchyny/gojq" "log" ) func main() { query, err := gojq.Parse(".[] | .foo") if err != nil { log.Fatalln(err) } code, err := gojq.Compile(query) if err != nil { log.Fatalln(err) } iter := code.Run([]interface{}{ nil, "string", 42, []interface{}{"foo"}, map[string]interface{}{"foo": 42}, }) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { fmt.Println(err) continue } fmt.Printf("%#v\n", v) } }
<nil> expected an object but got: string ("string") expected an object but got: number (42) expected an object but got: array (["foo"]) 42
func (*Code) Run ¶
Run runs the code with the variable values (which should be in the same order as the given variables using WithVariables) and returns a result iterator.
It is safe to call this method of a *Code in multiple goroutines.
Example ¶
Code:
package main import ( "fmt" "github.com/itchyny/gojq" "log" ) func main() { query, err := gojq.Parse(".foo") if err != nil { log.Fatalln(err) } code, err := gojq.Compile(query) if err != nil { log.Fatalln(err) } input := map[string]interface{}{"foo": 42} iter := code.Run(input) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { log.Fatalln(err) } fmt.Printf("%#v\n", v) } }
42
func (*Code) RunWithContext ¶
RunWithContext runs the code with context.
Example ¶
Code:
package main import ( "context" "fmt" "github.com/itchyny/gojq" "log" "time" ) func main() { query, err := gojq.Parse("def f: f; f, f") if err != nil { log.Fatalln(err) } code, err := gojq.Compile(query) if err != nil { log.Fatalln(err) } ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() iter := code.RunWithContext(ctx, nil) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { fmt.Println(err) continue } _ = v } }
context deadline exceeded
type CompilerOption ¶
type CompilerOption func(*compiler)
CompilerOption is a compiler option.
func WithEnvironLoader ¶
func WithEnvironLoader(environLoader func() []string) CompilerOption
WithEnvironLoader is a compiler option for environment variables loader. The OS environment variables are not accessible by default due to security reason. You can pass os.Environ if you allow to access it.
Example ¶
Code:
package main import ( "fmt" "github.com/itchyny/gojq" "log" ) func main() { query, err := gojq.Parse("env | keys[]") if err != nil { log.Fatalln(err) } code, err := gojq.Compile( query, gojq.WithEnvironLoader(func() []string { return []string{"foo=42", "bar=128"} }), ) if err != nil { log.Fatalln(err) } iter := code.Run(nil) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { log.Fatalln(err) } fmt.Printf("%#v\n", v) } }
"bar" "foo"
func WithFunction ¶
func WithFunction(name string, minarity int, maxarity int, f func(interface{}, []interface{}) interface{}) CompilerOption
WithFunction is a compiler option for adding a custom internal function. Specify the minimum and maximum count of the function arguments. These values should satisfy 0 <= minarity <= maxarity <= 30, otherwise panics. On handling numbers, you should take account to int, float64 and *big.Int.
Example ¶
Code:
package main import ( "encoding/json" "fmt" "log" "math/big" "strconv" "github.com/itchyny/gojq" ) func toFloat(x interface{}) (float64, bool) { switch x := x.(type) { case int: return float64(x), true case float64: return x, true case *big.Int: f, err := strconv.ParseFloat(x.String(), 64) return f, err == nil default: return 0.0, false } } func main() { query, err := gojq.Parse(".[] | f | f(3)") if err != nil { log.Fatalln(err) } code, err := gojq.Compile( query, gojq.WithFunction("f", 0, 1, func(x interface{}, xs []interface{}) interface{} { if x, ok := toFloat(x); ok { if len(xs) == 1 { if y, ok := toFloat(xs[0]); ok { x *= y } else { return fmt.Errorf("f cannot be applied to: %v, %v", x, xs) } } else { x += 2 } return x } return fmt.Errorf("f cannot be applied to: %v, %v", x, xs) }), ) if err != nil { log.Fatalln(err) } input := []interface{}{0, 1, 2.5, json.Number("10000000000000000000000000000000000000000")} iter := code.Run(input) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { log.Fatalln(err) } fmt.Printf("%#v\n", v) } }
6 9 13.5 3e+40
func WithInputIter ¶
func WithInputIter(inputIter Iter) CompilerOption
WithInputIter is a compiler option for input iterator used by input(s)/0. Note that input and inputs functions are not allowed by default. We have to distinguish the query input and the values for input(s) functions. For example, consider using inputs with --null-input. If you want to allow input(s) functions, create an Iter and use WithInputIter option.
Example ¶
Code:
package main import ( "encoding/json" "fmt" "io" "log" "strings" "github.com/itchyny/gojq" ) type testInputIter struct { dec *json.Decoder } func newTestInputIter(r io.Reader) *testInputIter { dec := json.NewDecoder(r) dec.UseNumber() return &testInputIter{dec: dec} } func (i *testInputIter) Next() (interface{}, bool) { var v interface{} if err := i.dec.Decode(&v); err != nil { if err == io.EOF { return nil, false } return err, true } return v, true } func main() { query, err := gojq.Parse("reduce inputs as $x (0; . + $x)") if err != nil { log.Fatalln(err) } code, err := gojq.Compile( query, gojq.WithInputIter( newTestInputIter(strings.NewReader("1 2 3 4 5")), ), ) if err != nil { log.Fatalln(err) } iter := code.Run(nil) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { log.Fatalln(err) } fmt.Printf("%#v\n", v) } }
15
func WithModuleLoader ¶
func WithModuleLoader(moduleLoader ModuleLoader) CompilerOption
WithModuleLoader is a compiler option for module loader. If you want to load modules from the filesystem, use NewModuleLoader.
Example ¶
Code:
package main import ( "fmt" "log" "github.com/itchyny/gojq" ) type moduleLoader struct{} func (*moduleLoader) LoadModule(name string) (*gojq.Query, error) { switch name { case "module1": return gojq.Parse(` module { name: "module1", test: 42 }; import "module2" as foo; def f: foo::f; `) case "module2": return gojq.Parse(` def f: .foo; `) } return nil, fmt.Errorf("module not found: %q", name) } func main() { query, err := gojq.Parse(` import "module1" as m; m::f `) if err != nil { log.Fatalln(err) } code, err := gojq.Compile( query, gojq.WithModuleLoader(&moduleLoader{}), ) if err != nil { log.Fatalln(err) } input := map[string]interface{}{"foo": 42} iter := code.Run(input) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { log.Fatalln(err) } fmt.Printf("%#v\n", v) } }
42
func WithVariables ¶
func WithVariables(variables []string) CompilerOption
WithVariables is a compiler option for variable names. The variables can be used in the query. You have to give the values to query.Run or code.Run in the same order.
Example ¶
Code:
package main import ( "fmt" "github.com/itchyny/gojq" "log" ) func main() { query, err := gojq.Parse("$x * 100 + $y, $z") if err != nil { log.Fatalln(err) } code, err := gojq.Compile( query, gojq.WithVariables([]string{ "$x", "$y", "$z", }), ) if err != nil { log.Fatalln(err) } iter := code.Run(nil, 12, 42, 128) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { log.Fatalln(err) } fmt.Printf("%#v\n", v) } }
1242 128
type ConstArray ¶
type ConstArray struct {
Elems []*ConstTerm
}
ConstArray ...
func (*ConstArray) String ¶
func (e *ConstArray) String() string
type ConstObject ¶
type ConstObject struct {
KeyVals []*ConstObjectKeyVal
}
ConstObject ...
func (*ConstObject) String ¶
func (e *ConstObject) String() string
func (*ConstObject) ToValue ¶
func (e *ConstObject) ToValue() map[string]interface{}
ToValue converts the object to map[string]interface{}.
type ConstObjectKeyVal ¶
ConstObjectKeyVal ...
func (*ConstObjectKeyVal) String ¶
func (e *ConstObjectKeyVal) String() string
type ConstTerm ¶
type ConstTerm struct { Object *ConstObject Array *ConstArray Number string Str string Null bool True bool False bool }
ConstTerm ...
type Import ¶
type Import struct { ImportPath string ImportAlias string IncludePath string Meta *ConstObject }
Import ...
type Iter ¶
type Iter interface {
Next() (interface{}, bool)
}
Iter is an interface for an iterator.
type ModuleLoader ¶
type ModuleLoader interface { }
ModuleLoader is an interface for loading modules.
Implement following optional methods. Use NewModuleLoader to load local modules.
LoadModule(string) (*Query, error) LoadModuleWithMeta(string, map[string]interface{}) (*Query, error) LoadInitModules() ([]*Query, error) LoadJSON(string) (interface{}, error) LoadJSONWithMeta(string, map[string]interface{}) (interface{}, error)
func NewModuleLoader ¶
func NewModuleLoader(paths []string) ModuleLoader
NewModuleLoader creates a new ModuleLoader reading local modules in the paths.
type ObjectKeyVal ¶
type ObjectKeyVal struct { Key string KeyString *String KeyQuery *Query Val *ObjectVal KeyOnly string KeyOnlyString *String }
ObjectKeyVal ...
func (*ObjectKeyVal) String ¶
func (e *ObjectKeyVal) String() string
type Operator ¶
type Operator int
Operator ...
const ( OpPipe Operator = iota + 1 OpComma OpAdd OpSub OpMul OpDiv OpMod OpEq OpNe OpGt OpLt OpGe OpLe OpAnd OpOr OpAlt OpAssign OpModify OpUpdateAdd OpUpdateSub OpUpdateMul OpUpdateDiv OpUpdateMod OpUpdateAlt )
Operators ...
type Pattern ¶
type Pattern struct { Name string Array []*Pattern Object []*PatternObject }
Pattern ...
type PatternObject ¶
type PatternObject struct { Key string KeyString *String KeyQuery *Query Val *Pattern KeyOnly string }
PatternObject ...
func (*PatternObject) String ¶
func (e *PatternObject) String() string
type Query ¶
type Query struct { Meta *ConstObject Imports []*Import FuncDefs []*FuncDef Term *Term Left *Query Op Operator Right *Query Func string }
Query represents the abstract syntax tree of a jq query.
func (*Query) Run ¶
Run the query.
It is safe to call this method of a *Query in multiple goroutines.
Example ¶
Code:
package main import ( "fmt" "github.com/itchyny/gojq" "log" ) func main() { query, err := gojq.Parse(".foo | ..") if err != nil { log.Fatalln(err) } input := map[string]interface{}{"foo": []interface{}{1, 2, 3}} iter := query.Run(input) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { log.Fatalln(err) } fmt.Printf("%#v\n", v) } }
[]interface {}{1, 2, 3} 1 2 3
func (*Query) RunWithContext ¶
RunWithContext runs the query with context.
Example ¶
Code:
package main import ( "context" "fmt" "github.com/itchyny/gojq" "log" "time" ) func main() { query, err := gojq.Parse("def f: f; f, f") if err != nil { log.Fatalln(err) } ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() iter := query.RunWithContext(ctx, nil) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { fmt.Println(err) continue } _ = v } }
context deadline exceeded
type Term ¶
type Term struct { Type TermType Index *Index Func *Func Object *Object Array *Array Number string Unary *Unary Format string Str *String If *If Try *Try Reduce *Reduce Foreach *Foreach Label *Label Break string Query *Query SuffixList []*Suffix }
Term ...
type TermType ¶
type TermType int
TermType represents the type of Term.
const ( TermTypeIdentity TermType = iota + 1 TermTypeRecurse TermTypeNull TermTypeTrue TermTypeFalse TermTypeIndex TermTypeFunc TermTypeObject TermTypeArray TermTypeNumber TermTypeUnary TermTypeFormat TermTypeString TermTypeIf TermTypeTry TermTypeReduce TermTypeForeach TermTypeLabel TermTypeBreak TermTypeQuery )
TermType list.