gabs
gabs copied to clipboard
For parsing, creating and editing unknown or dynamic JSON in Go

Gabs is a small utility for dealing with dynamic or unknown JSON structures in Go. It's pretty much just a helpful wrapper for navigating hierarchies of map[string]interface{} objects provided by the encoding/json package. It does nothing spectacular apart from being fabulous.
If you're migrating from version 1 check out migration.md for guidance.
Use
Import
Using modules:
import (
"github.com/Jeffail/gabs/v2"
)
Without modules:
import (
"github.com/Jeffail/gabs"
)
Parsing and searching JSON
jsonParsed, err := gabs.ParseJSON([]byte(`{
"outter":{
"inner":{
"value1":10,
"value2":22
},
"alsoInner":{
"value1":20,
"array1":[
30, 40
]
}
}
}`))
if err != nil {
panic(err)
}
var value float64
var ok bool
value, ok = jsonParsed.Path("outter.inner.value1").Data().(float64)
// value == 10.0, ok == true
value, ok = jsonParsed.Search("outter", "inner", "value1").Data().(float64)
// value == 10.0, ok == true
value, ok = jsonParsed.Search("outter", "alsoInner", "array1", "1").Data().(float64)
// value == 40.0, ok == true
gObj, err := jsonParsed.JSONPointer("/outter/alsoInner/array1/1")
if err != nil {
panic(err)
}
value, ok = gObj.Data().(float64)
// value == 40.0, ok == true
value, ok = jsonParsed.Path("does.not.exist").Data().(float64)
// value == 0.0, ok == false
exists := jsonParsed.Exists("outter", "inner", "value1")
// exists == true
exists = jsonParsed.ExistsP("does.not.exist")
// exists == false
Iterating objects
jsonParsed, err := gabs.ParseJSON([]byte(`{"object":{"first":1,"second":2,"third":3}}`))
if err != nil {
panic(err)
}
// S is shorthand for Search
for key, child := range jsonParsed.S("object").ChildrenMap() {
fmt.Printf("key: %v, value: %v\n", key, child.Data().(float64))
}
Iterating arrays
jsonParsed, err := gabs.ParseJSON([]byte(`{"array":["first","second","third"]}`))
if err != nil {
panic(err)
}
for _, child := range jsonParsed.S("array").Children() {
fmt.Println(child.Data().(string))
}
Will print:
first
second
third
Children() will return all children of an array in order. This also works on objects, however, the children will be returned in a random order.
Searching through arrays
If your structure contains arrays you must target an index in your search.
jsonParsed, err := gabs.ParseJSON([]byte(`{"array":[{"value":1},{"value":2},{"value":3}]}`))
if err != nil {
panic(err)
}
fmt.Println(jsonParsed.Path("array.1.value").String())
Will print 2.
Generating JSON
jsonObj := gabs.New()
// or gabs.Wrap(jsonObject) to work on an existing map[string]interface{}
jsonObj.Set(10, "outter", "inner", "value")
jsonObj.SetP(20, "outter.inner.value2")
jsonObj.Set(30, "outter", "inner2", "value3")
fmt.Println(jsonObj.String())
Will print:
{"outter":{"inner":{"value":10,"value2":20},"inner2":{"value3":30}}}
To pretty-print:
fmt.Println(jsonObj.StringIndent("", " "))
Will print:
{
"outter": {
"inner": {
"value": 10,
"value2": 20
},
"inner2": {
"value3": 30
}
}
}
Generating Arrays
jsonObj := gabs.New()
jsonObj.Array("foo", "array")
// Or .ArrayP("foo.array")
jsonObj.ArrayAppend(10, "foo", "array")
jsonObj.ArrayAppend(20, "foo", "array")
jsonObj.ArrayAppend(30, "foo", "array")
fmt.Println(jsonObj.String())
Will print:
{"foo":{"array":[10,20,30]}}
Working with arrays by index:
jsonObj := gabs.New()
// Create an array with the length of 3
jsonObj.ArrayOfSize(3, "foo")
jsonObj.S("foo").SetIndex("test1", 0)
jsonObj.S("foo").SetIndex("test2", 1)
// Create an embedded array with the length of 3
jsonObj.S("foo").ArrayOfSizeI(3, 2)
jsonObj.S("foo").Index(2).SetIndex(1, 0)
jsonObj.S("foo").Index(2).SetIndex(2, 1)
jsonObj.S("foo").Index(2).SetIndex(3, 2)
fmt.Println(jsonObj.String())
Will print:
{"foo":["test1","test2",[1,2,3]]}
Converting back to JSON
This is the easiest part:
jsonParsedObj, _ := gabs.ParseJSON([]byte(`{
"outter":{
"values":{
"first":10,
"second":11
}
},
"outter2":"hello world"
}`))
jsonOutput := jsonParsedObj.String()
// Becomes `{"outter":{"values":{"first":10,"second":11}},"outter2":"hello world"}`
And to serialize a specific segment is as simple as:
jsonParsedObj := gabs.ParseJSON([]byte(`{
"outter":{
"values":{
"first":10,
"second":11
}
},
"outter2":"hello world"
}`))
jsonOutput := jsonParsedObj.Search("outter").String()
// Becomes `{"values":{"first":10,"second":11}}`
Merge two containers
You can merge a JSON structure into an existing one, where collisions will be converted into a JSON array.
jsonParsed1, _ := ParseJSON([]byte(`{"outter":{"value1":"one"}}`))
jsonParsed2, _ := ParseJSON([]byte(`{"outter":{"inner":{"value3":"three"}},"outter2":{"value2":"two"}}`))
jsonParsed1.Merge(jsonParsed2)
// Becomes `{"outter":{"inner":{"value3":"three"},"value1":"one"},"outter2":{"value2":"two"}}`
Arrays are merged:
jsonParsed1, _ := ParseJSON([]byte(`{"array":["one"]}`))
jsonParsed2, _ := ParseJSON([]byte(`{"array":["two"]}`))
jsonParsed1.Merge(jsonParsed2)
// Becomes `{"array":["one", "two"]}`
Parsing Numbers
Gabs uses the json package under the bonnet, which by default will parse all number values into float64. If you need to parse Int values then you should use a json.Decoder:
sample := []byte(`{"test":{"int":10,"float":6.66}}`)
dec := json.NewDecoder(bytes.NewReader(sample))
dec.UseNumber()
val, err := gabs.ParseJSONDecoder(dec)
if err != nil {
t.Errorf("Failed to parse: %v", err)
return
}
intValue, err := val.Path("test.int").Data().(json.Number).Int64()