Query Interface JSON and set or delete values easily
IJSON is a small but effective utility to deal with dynamic or unknown JSON structures in Go. It's a helpful wrapper for navigating hierarchies of map[string]interface{}
OR []interface{}
. It is the best solution for one time data access and manipulation.
Other libraries parse the whole json structure in their own format and again to interface if required, not suitable if you have interface{}
as input and want the output in same format.
Note - This is not a json parser. It just plays with raw interface data.
- Very fast in accessing and manipulating top level values.
- Avoids parsing whole JSON structure to intermediate format. Saves allocations.
- Easy API to perform query, set or delete operations on raw interface data.
- One line syntax to chain multiple operations together.
- Not suitable if you want to perform multiple operations on same data.
go get -u github.com/akshaybharambe14/ijson
This package provides two types of functions. The functions suffixed with <action>P
accept a path separated by "."
.
Ex. "#0.friends.#~name"
package main
import (
"fmt"
"github.com/akshaybharambe14/ijson"
)
var dataBytes = []byte(`
[
{
"index": 0,
"friends": [
{
"id": 0,
"name": "Justine Bird"
},
{
"id": 0,
"name": "Justine Bird"
},
{
"id": 1,
"name": "Marianne Rutledge"
}
]
}
]
`)
func main() {
r := ijson.ParseByes(dataBytes).
GetP("#0.friends.#~name"). // list the friend names for 0th record -
// []interface {}{"Justine Bird", "Justine Bird", "Marianne Rutledge"}
Del("#0"). // delete 0th record
// []interface {}{"Marianne Rutledge", "Justine Bird"}
Set("tom", "#") // append "tom" in the list
// []interface {}{"Marianne Rutledge", "Justine Bird", "tom"}
fmt.Printf("%#v\n", r.Value())
// returns error if the data type differs than the type expected by query
fmt.Println(r.Set(1, "name").Error())
}
IJSON follows a specific path syntax to access the data. The implementation sticks to the analogy that, user knows the path. So if caller wants to access an index, the underlying data must be an array otherwise, an error will be returned.
Use functions and methods suffixed by P
to provide a "."
separated path.
{
"index": 0,
"name": { "first": "Tom", "last": "Anderson" },
"friends": [
{ "id": 1, "name": "Justine Bird" },
{ "id": 2, "name": "Justine Rutledge" },
{ "id": 3, "name": "Marianne Rutledge" }
]
}
Summary of get operations on above data.
"name.last" >> "Anderson" // GET "last" field from "name" object
"friends.#" >> 3 // GET length of "friends" array
"friends.#~id" >> [ 1, 2, 3 ] // GET all values of "id" field from "friends" array
"friends.#0" >> { "id": 1, "name": "Justine Bird" } // GET "0th" element from "friends" array
Set overwrites the existing data. An error will be returned if the data does not match the query. If the data is nil
, it will create the structure.
There is an alternative for datatype mismatch. Use SetF
instead of Set
function. It will forcefully replace the existing with provided.
Following path syntax sets "Anderson" as a value in empty structure.
"name.last" >> { "name": { "last": "Anderson" } } // Create an object and SET value of "last" field in "name" object
"#2" >> ["", "", "Anderson"] // Create an array and SET value at "2nd" index
"friends.#" >> { "friends": [ "Anderson" ] } // Create an object and APPEND to "friends" array
While deleting at an index, you have two options. By default, deletes does not preserve order. This helps to save unnecessary allocations as it just replaces the data at given index with last element. Refer following syntax for details.
{
"index": 0,
"friends": ["Justine Bird", "Justine Rutledge", "Marianne Rutledge"]
}
Summary of delete operations on above data.
"index" >> { "friends": [ "Justine Bird", "Justine Rutledge", "Marianne Rutledge" ] } // DELETE "index" field
"friends.#" >> { "index": 0, "friends": [ "Justine Bird", "Justine Rutledge" ] } // DELETE last element from "friends" array
"friends.#0" >> { "index": 0, "friends": [ "Marianne Rutledge", "Justine Rutledge" ] } // DELETE "0th" element from "friends" array WITHOUT preserving order
"friends.#~0" >> { "index": 0, "friends": [ "Justine Rutledge", "Marianne Rutledge" ] } // DELETE "0th" element from "friends" array WITH preserving order
You can chain multiple operations and check if it succeeds or fails.
r := ijson.New(data).Get("#0", "friends", "#~name").Del("#0").Set(value, "#")
if r.Error() != nil {
...
}
// access value
_ = r.Value()
This package uses standard library encoding/json as a json parser. We already have a very wide range of json parsers. I would recommend GJSON. It is probably the fastest, as far as I know.
See ijson.ParseBytes()
and ijson.Parse()
functions.
Please check following awesome projects, you might find a better match for you.
Akshay Bharambe @akshaybharambe1
IJSON source code is available under the MIT License.