Abstract JSON for golang with JSONPath support

Overview

Abstract JSON

Build Status Go Report Card GoDoc Coverage Status Awesome

Abstract JSON is a small golang package provides a parser for JSON with support of JSONPath, in case when you are not sure in its structure.

Method Unmarshal will scan all the byte slice to create a root node of JSON structure, with all its behaviors.

Method Marshal will serialize current Node object to JSON structure.

Each Node has its own type and calculated value, which will be calculated on demand. Calculated value saves in atomic.Value, so it's thread safe.

Method JSONPath will returns slice of found elements in current JSON data, by JSONPath request.

Compare with other solutions

Check the cburgmer/json-path-comparison project.

Usage

Playground

package main

import (
	"fmt"
	"github.com/spyzhov/ajson"
)

func main() {
	json := []byte(`...`)

	root, _ := ajson.Unmarshal(json)
	nodes, _ := root.JSONPath("$..price")
	for _, node := range nodes {
		node.SetNumeric(node.MustNumeric() * 1.25)
		node.Parent().AppendObject("currency", ajson.StringNode("", "EUR"))
	}
	result, _ := ajson.Marshal(root)

	fmt.Printf("%s", result)
}

Console application

You can download ajson cli from the release page, or install from the source:

go get github.com/spyzhov/ajson/cmd/[email protected]

Usage:

Usage: ajson "jsonpath" ["input"]
  Read JSON and evaluate it with JSONPath.
Argument:
  jsonpath   Valid JSONPath or evaluate string (Examples: "$..[?(@.price)]", "$..price", "avg($..price)")
  input      Path to the JSON file. Leave it blank to use STDIN.

Examples:

  ajson "avg($..registered.age)" "https://randomuser.me/api/?results=5000"
  ajson "$.results.*.name" "https://randomuser.me/api/?results=10"
  curl -s "https://randomuser.me/api/?results=10" | ajson "$..coordinates"
  ajson "$" example.json
  echo "3" | ajson "2 * pi * $"

JSONPath

Current package supports JSONPath selection described at http://goessner.net/articles/JsonPath/.

JSONPath expressions always refer to a JSON structure in the same way as XPath expression are used in combination with an XML document. Since a JSON structure is usually anonymous and doesn't necessarily have a "root member object" JSONPath assumes the abstract name $ assigned to the outer level object.

JSONPath expressions can use the dot–notation

$.store.book[0].title

or the bracket–notation

$['store']['book'][0]['title']

for input paths. Internal or output paths will always be converted to the more general bracket–notation.

JSONPath allows the wildcard symbol * for member names and array indices. It borrows the descendant operator .. from E4X and the array slice syntax proposal [start:end:step] from ECMASCRIPT 4.

Expressions of the underlying scripting language (<expr>) can be used as an alternative to explicit names or indices as in

$.store.book[(@.length-1)].title

using the symbol @ for the current object. Filter expressions are supported via the syntax ?(<boolean expr>) as in

$.store.book[?(@.price < 10)].title

Here is a complete overview and a side by side comparison of the JSONPath syntax elements with its XPath counterparts.

JSONPath Description
$ the root object/element
@ the current object/element
. or [] child operator
.. recursive descent. JSONPath borrows this syntax from E4X.
* wildcard. All objects/elements regardless their names.
[] subscript operator. XPath uses it to iterate over element collections and for predicates. In Javascript and JSON it is the native array operator.
[,] Union operator in XPath results in a combination of node sets. JSONPath allows alternate names or array indices as a set.
[start:end:step] array slice operator borrowed from ES4.
?() applies a filter (script) expression.
() script expression, using the underlying script engine.

Script engine

Predefined constant

Package has several predefined constants.

 e       math.E     float64
 pi      math.Pi    float64
 phi     math.Phi   float64
 
 sqrt2     math.Sqrt2   float64
 sqrte     math.SqrtE   float64
 sqrtpi    math.SqrtPi  float64
 sqrtphi   math.SqrtPhi float64
 
 ln2     math.Ln2    float64
 log2e   math.Log2E  float64
 ln10    math.Ln10   float64
 log10e  math.Log10E float64
      
 true    true       bool
 false   false      bool
 null    nil        interface{}

You are free to add new one with function AddConstant:

    AddConstant("c", NumericNode("speed of light in vacuum", 299_792_458))

Examples

Using `true` in path

Playground

package main

import (
	"fmt"
	
	"github.com/spyzhov/ajson"
)

func main() {
	json := []byte(`{"foo": [true, null, false, 1, "bar", true, 1e3], "bar": [true, "baz", false]}`)
	result, _ := ajson.JSONPath(json, `$..[?(@ == true)]`)
	fmt.Printf("Count of `true` values: %d", len(result))
}

Output:

Count of `true` values: 3
Using `null` in eval

Playground

package main

import (
	"fmt"
	
	"github.com/spyzhov/ajson"
)

func main() {
	json := []byte(`{"foo": [true, null, false, 1, "bar", true, 1e3], "bar": [true, "baz", false]}`)
	result, _ := ajson.JSONPath(json, `$..[?(@ == true)]`)
	fmt.Printf("Count of `true` values: %d", len(result))
}

Output:

Count of `true` values: 3

Supported operations

Package has several predefined operators.

Operator precedence

Precedence    Operator
    6	    	  **
    5             *  /  %  <<  >>  &  &^
    4             +  -  |  ^
    3             ==  !=  <  <=  >  >= =~
    2             &&
    1             ||

Arithmetic operators

**   power                  integers, floats
+    sum                    integers, floats, strings
-    difference             integers, floats
*    product                integers, floats
/    quotient               integers, floats
%    remainder              integers

&    bitwise AND            integers
|    bitwise OR             integers
^    bitwise XOR            integers
&^   bit clear (AND NOT)    integers

<<   left shift             integer << unsigned integer
>>   right shift            integer >> unsigned integer

==  equals                  any
!=  not equals              any
<   less                    any
<=  less or equals          any
>   larger                  any
>=  larger or equals        any
=~  equals regex string     strings

You are free to add new one with function AddOperation:

	AddOperation("<>", 3, false, func(left *ajson.Node, right *ajson.Node) (node *ajson.Node, err error) {
		result, err := left.Eq(right)
		if err != nil {
			return nil, err
		}
		return BoolNode("neq", !result), nil
	})

Examples

Using `regex` operator

Playground

package main

import (
	"fmt"
	
	"github.com/spyzhov/ajson"
)

func main() {
	json := []byte(`[{"name":"Foo","mail":"[email protected]"},{"name":"bar","mail":"[email protected]"}]`)
	result, err := ajson.JSONPath(json, `$.[?(@.mail =~ '[email protected]\\.com')]`)
	if err != nil {
		panic(err)
	}
	fmt.Printf("JSON: %s", result[0].Source())
	// Output:
	// JSON: {"name":"Foo","mail":"[email protected]"}
}

Output:

JSON: {"name":"Foo","mail":"[email protected]"}

Supported functions

Package has several predefined functions.

abs          math.Abs          integers, floats
acos         math.Acos         integers, floats
acosh        math.Acosh        integers, floats
asin         math.Asin         integers, floats
asinh        math.Asinh        integers, floats
atan         math.Atan         integers, floats
atanh        math.Atanh        integers, floats
avg          Average           array of integers or floats
cbrt         math.Cbrt         integers, floats
ceil         math.Ceil         integers, floats
cos          math.Cos          integers, floats
cosh         math.Cosh         integers, floats
erf          math.Erf          integers, floats
erfc         math.Erfc         integers, floats
erfcinv      math.Erfcinv      integers, floats
erfinv       math.Erfinv       integers, floats
exp          math.Exp          integers, floats
exp2         math.Exp2         integers, floats
expm1        math.Expm1        integers, floats
factorial    N!                unsigned integer
floor        math.Floor        integers, floats
gamma        math.Gamma        integers, floats
j0           math.J0           integers, floats
j1           math.J1           integers, floats
length       len               array
log          math.Log          integers, floats
log10        math.Log10        integers, floats
log1p        math.Log1p        integers, floats
log2         math.Log2         integers, floats
logb         math.Logb         integers, floats
not          not               any
pow10        math.Pow10        integer
round        math.Round        integers, floats
roundtoeven  math.RoundToEven  integers, floats
sin          math.Sin          integers, floats
sinh         math.Sinh         integers, floats
sum          Sum               array of integers or floats
sqrt         math.Sqrt         integers, floats
tan          math.Tan          integers, floats
tanh         math.Tanh         integers, floats
trunc        math.Trunc        integers, floats
y0           math.Y0           integers, floats
y1           math.Y1           integers, floats

You are free to add new one with function AddFunction:

	AddFunction("trim", func(node *ajson.Node) (result *Node, err error) {
		if node.IsString() {
			return StringNode("trim", strings.TrimSpace(node.MustString())), nil
		}
		return
	})

Examples

Using `avg` for array

Playground

package main

import (
	"fmt"
	
	"github.com/spyzhov/ajson"
)

func main() {
	json := []byte(`{"prices": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}`)
	root, err := ajson.Unmarshal(json)
	if err != nil {
		panic(err)
	}
	result, err := ajson.Eval(root, `avg($.prices)`)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Avg price: %0.1f", result.MustNumeric())
	// Output:
	// Avg price: 5.5
}

Output:

Avg price: 5.5

Examples

Calculating AVG(price) when object is heterogeneous.

{
  "store": {
    "book": [
      {
        "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      {
        "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      {
        "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      {
        "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    },
    "tools": null
  }
}

Unmarshal

Playground

package main

import (
	"fmt"
	"github.com/spyzhov/ajson"
)

func main() {
	data := []byte(`{"store": {"book": [
{"category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95}, 
{"category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99}, 
{"category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99}, 
{"category": "fiction", "author": "J. R. R. Tolkien", "title": "The Lord of the Rings", "isbn": "0-395-19395-8", "price": 22.99}], 
"bicycle": {"color": "red", "price": 19.95}, "tools": null}}`)

	root, err := ajson.Unmarshal(data)
	if err != nil {
		panic(err)
	}

	store := root.MustKey("store").MustObject()

	var prices float64
	size := 0
	for _, objects := range store {
		if objects.IsArray() && objects.Size() > 0 {
			size += objects.Size()
			for _, object := range objects.MustArray() {
				prices += object.MustKey("price").MustNumeric()
			}
		} else if objects.IsObject() && objects.HasKey("price") {
			size++
			prices += objects.MustKey("price").MustNumeric()
		}
	}

	if size > 0 {
		fmt.Println("AVG price:", prices/float64(size))
	} else {
		fmt.Println("AVG price:", 0)
	}
}

JSONPath:

Playground

package main

import (
	"fmt"
	"github.com/spyzhov/ajson"
)

func main() {
	data := []byte(`{"store": {"book": [
{"category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95}, 
{"category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99}, 
{"category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99}, 
{"category": "fiction", "author": "J. R. R. Tolkien", "title": "The Lord of the Rings", "isbn": "0-395-19395-8", "price": 22.99}], 
"bicycle": {"color": "red", "price": 19.95}, "tools": null}}`)

	nodes, err := ajson.JSONPath(data, "$..price")
	if err != nil {
		panic(err)
	}

	var prices float64
	size := len(nodes)
	for _, node := range nodes {
		prices += node.MustNumeric()
	}

	if size > 0 {
		fmt.Println("AVG price:", prices/float64(size))
	} else {
		fmt.Println("AVG price:", 0)
	}
}

Eval

Playground

package main

import (
	"fmt"
	"github.com/spyzhov/ajson"
)

func main() {
	json := []byte(`{"store": {"book": [
{"category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95}, 
{"category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99}, 
{"category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99}, 
{"category": "fiction", "author": "J. R. R. Tolkien", "title": "The Lord of the Rings", "isbn": "0-395-19395-8", "price": 22.99}], 
"bicycle": {"color": "red", "price": 19.95}, "tools": null}}`)
	root, err := ajson.Unmarshal(json)
	if err != nil {
		panic(err)
	}
	result, err := ajson.Eval(root, "avg($..price)")
	if err != nil {
		panic(err)
	}
	fmt.Println("AVG price:", result.MustNumeric())
}

Marshal

Playground

package main

import (
	"fmt"
	"github.com/spyzhov/ajson"
)

func main() {
	json := []byte(`{"store": {"book": [
{"category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95}, 
{"category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99}, 
{"category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99}, 
{"category": "fiction", "author": "J. R. R. Tolkien", "title": "The Lord of the Rings", "isbn": "0-395-19395-8", "price": 22.99}], 
"bicycle": {"color": "red", "price": 19.95}, "tools": null}}`)
	root := ajson.Must(ajson.Unmarshal(json))
	result := ajson.Must(ajson.Eval(root, "avg($..price)"))
	err := root.AppendObject("price(avg)", result)
	if err != nil {
		panic(err)
	}
	marshalled, err := ajson.Marshal(root)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s", marshalled)
}

Benchmarks

Current package is comparable with encoding/json package.

Test data:

{ "store": {
    "book": [ 
      { "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      { "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      { "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      { "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}

JSONPath: $.store..price

$ go test -bench=. -cpu=1 -benchmem
goos: linux
goarch: amd64
pkg: github.com/spyzhov/ajson
BenchmarkUnmarshal_AJSON          138032              8762 ns/op            5344 B/op         95 allocs/op
BenchmarkUnmarshal_JSON           117423             10502 ns/op             968 B/op         31 allocs/op
BenchmarkJSONPath_all_prices       80908             14394 ns/op            7128 B/op        153 allocs/op

License

MIT licensed. See the LICENSE file for details.

Issues
  • Results do not match other implementations

    Results do not match other implementations

    The following queries provide results that do not match those of other implementations of JSONPath (compare https://cburgmer.github.io/json-path-comparison/):

    • [ ] $["key"] Input:

      {"key": "value"}
      

      Expected output:

      ["value"]
      

      Actual output:

      []
      
    • [ ] $[?(@['key']==42)] Input:

      [{"key": 0}, {"key": 42}, {"key": -1}, {"key": 41}, {"key": 43}, {"key": 42.0001}, {"key": 41.9999}, {"key": 100}, {"some": "value"}]
      

      Expected output:

      [{"key": 42}]
      

      Error:

      wrong symbol '=' at 12
      
    • [ ] $[?(@.key=="some.value")] Input:

      [{"key": "some"}, {"key": "value"}, {"key": "some.value"}]
      

      Expected output:

      [{"key": "some.value"}]
      

      Error:

      wrong request: wrong request: ?(@.key=="some.value")
      

    For reference, the output was generated by the program in https://github.com/cburgmer/json-path-comparison/tree/master/implementations/Golang_github.com-spyzhov-ajson.

    bug 
    opened by cburgmer 10
  • How about expose deReference function

    How about expose deReference function

    The ajson.JSONPath function build commands and unmarshal node which can be reused, but deReference is not public. How about expose deReference function, add DeReference function, so the developers can reuse the parsed commands and node object

    good first issue 
    opened by cfhamlet 3
  • SetArray not working

    SetArray not working

    Hello. I need replace node in array For example in JSON {"key": [1, 2, 3]} replace second index to another Node

    vals := parent.MustArray()
    vals[2] = anotherNode
    parent.SetArray(vals)
    

    and as result of Marshal received empty array {"key": []}

    bug 
    opened by nikalexey 3
  • M1 build

    M1 build

    This is not for myself, as I have Go installed, but not all of my coworkers do. I have used ajson to validate the contents of a json file for work and coworkers will require an M1 build. I can provide that for them if needed but if you can add it to your build set I would be grateful. I very much enjoy using the cli tool and look forward to using the library as well. Thank-you.

    opened by imarsman 2
  • node.Value() doesn't recurse

    node.Value() doesn't recurse

    When looking up $.a on {"a": {"b":42}} and using node.Value() to derive the value, I receive {"b":{}} instead of the expected {"b":42}. Am I doing it right?

    invalid 
    opened by cburgmer 2
  • Updated marshal encoder to order the object keys before rendering

    Updated marshal encoder to order the object keys before rendering

    In order to have consistent JSON output, when rendering out the Object type nodes through the encoder, this update orders the keys before writing the bytes so that all Properties are deterministic. This mimics the same behaviour as the built-in JSON marshaller: json.Marshal.

    opened by jono-tt 1
  • length of a filtered list

    length of a filtered list

    I just discovered this project a few hours ago, and very impressed by this. I've been playing with ajson and discovered (or not discovered) a way to print the length of a filtered list. For example:

    The following command outputs a list, but if I want to print out the number of results returned, I found I cannot use the .length operator there. Am I missing something here or is it that feature is missing?

    ajson '$.results[?(@.gender=="female")]' "https://randomuser.me/api/?results=10"
    

    Looking to achieve a result like this

    ./ajson '$.results[?(@.gender=="female")]' "https://randomuser.me/api/?results=10" | jq 'length'
    
    question 
    opened by prashanth-hegde 1
  • JSONPath comparison and standardisation

    JSONPath comparison and standardisation

    @cburgmer's JSONPath comparison project is currently discussing some issues relating to a proposed implementation of JSONPath known as "Proposal A". May I encourage you to get involved if you are interested in influencing the direction or, indeed, if you want to make your own proposals.

    Similarly, please join us in slack (invitation) for informal discussions on the comparison project and potential JSONPath standardisation.

    opened by glyn 1
  • Make JSONPath work with unmarshalled JSON

    Make JSONPath work with unmarshalled JSON

    It's an interesting library, I wish it was even more abstract and allowed to use JSONPath features on already unmarshalled JSON, like:

    if err := json.Unmarshal(someJSON, &v); err != nil {
      log.Fatal(err)
    }
    
    objs, err := ajson.JSONPath2(v, path)
    if err != nil {
      log.Fatal(err)
    }
    

    Potentially JSONPath2 can return a list of *Node, but it's important for Nodes to keep connection with original "&v" so if I modify a node the modification is propagated to v.

    I looked at the code and couldn't find an easy way to do that, because currently jsonpath and Node are tightly coupled with marshalling/unmarshilling optimizations, like keeping reference to the source JSON []byte, mark nodes dirty etc.

    WDYT?

    question 
    opened by oleksiys 1
  • #35 implemented `node.Set` and `node.SetNode` methods

    #35 implemented `node.Set` and `node.SetNode` methods

    • Implemented node.Set and node.SetNode methods for #35
    • Added go1.17.x version to test
    • Updated release version
    • Added default results for (*Node)(nil) methods
    opened by spyzhov 0
  • Version v1.0.0

    Version v1.0.0

    Draft for the Version v1.0.0 milestone

    1. Create sub-package ajson/jsonpath with the main types and functions:
      package jsonpath
      
      type JSONPath struct {
        Tokens []Token
      }
      type Token interface {
        ...
      }
      var _ Token = (*JSONPath)(nil)
      
      func Compile(jsonpath string) (*JSONPath, error)
      func MustCompile(jsonpath string) *JSONPath
      

      Add changes in the main package:

      package ajson
      
      func JSONPathApply(*Node, *jsonpath.JSONPath, ...Options) ([Nodes, error)
      
      • create all necessary Token types
      • add an ability to use array and object types
      • think about variables with placeholders for it
    2. Segregation for types and packages.
      • think about jmath;
      • move all system parts in the internal package;
    3. Remove threadsafe part, as it is unused.
    4. Add public interfaces:
      • for typed Node, such as StringNode, ArrayNode, etc.
      • IterableNode interface for ArrayNode and ObjectNode
      • for []*Node add alias type Nodes []Node
        type Nodes []Node
        func (n Nodes) First() Node
        func (n Nodes) Last() Node
        func (n Nodes) Error() error // ???
        func (n Nodes) JSONPath(...) (Nodes, error)
        func (n Nodes) Clone() Nodes
        func (n Nodes) Size() int
        
        func (n Nodes) Map(func (*Node) *Node) Nodes
        func (n Nodes) Filter(func (*Node) bool) Nodes
        
        JSONPath(...) (Nodes, error)
        
    5. Add a fake type Date (discussed)
      type Date (String)
      
      fun date(string|numeric, format) Date
      fun now() Date
      
      # usage:
      $..[?(date(@.timestamp) > now())]
      $..[?(date(@.timestamp) + date("1 hour", "%H hour") < now())]
      
    6. Add functional methods
      Chain(Node, ...func(Node) (Node, error) ... ) (Node, error)
      
      Filter(Node, func(Node) bool) (Node, error)
      Map(Node, func(Node) (Node, error)) (Node, error)
      Reduce...
      
      All(Node) bool
      Any(Node) bool
      
    7. Math:
      • Add additional jsonb operators from https://www.postgresql.org/docs/9.5/functions-json.html
      • Change Funtion type to:
        type Function func(node ...*Node) (result *Node, err error)
        
      • think about function: not => [email protected]
      • add math functions:
        first(node ...*Node) *Node
        get(index_or_key String|Integer, node ...*Node) *Node
        
      • add operator in => value in [array]
      • add functions max(array|object), min(array|object), avg(array|object).
    8. Add Buffer interface, which could be based on (1) []byte and (2) io.Readable interface;
    9. Add JSONPATH config values, like:
      • AllowScript to allow using !() outside of the filter section;
      • SpecificationVersion
      • etc.
    10. CLI:
      • Add support for multiline JSON. Flag -m/-multiline. Example: JSON logs in Docker.
        docker logs service | ajson -m "$.[?(@.level = 'error')]"
        
      • Add support for stream JSON. Flag -f/-stream. It will do the output as soon as the value was found.
      • Add pretty print support. Flag -pretty
      • Add suttort to remove given pattern from the result. Flag -reverse/-r
    11. Mutations:
      • Think about to add lock function and lock nodes on modifications.
        func (n Node) Set(value interface{}) error {
            defer lock(n).unlock()
            // todo
        }
        

        Use it for root (if needed)

        func (n Node) Set(value interface{}) error {
            defer lock(n.root()).unlock()
            // todo
        }
        
      • Add an Option to create a *Node if it was not found during the JSONPath execution. Example:
        nodes, _ := root.JSONPath(`$.foo.bar`, ajson.FindOrCreate)
        

        With the given json value {} the result output for the root will be {"foo":{"bar":null}}

    12. Documentation:
      • README.md
      • Examples
      • Add Wiki pages
      • readthedocs.io
    opened by spyzhov 4
  • Enhanced support for updating json using jsonpath

    Enhanced support for updating json using jsonpath

    This is an interesitng project! It seems like it's the only jsonpath library that supports mutation and not just retrieval. That said, the mutation API/behavior could be improved.

    I'd like to be able to mutate json using jsonpath. This leads to two asks:

    1. I'd also like to be able to pass an interface{} to a Node.Set function and have it handle conversion to the right type, erroring if the type is invalid. I think this just means exporting the internal update method: https://github.com/spyzhov/ajson/blob/master/node_mutations.go#L147
    2. If the node exists, ajson makes mutation easy by supporting Set fields on Node. However, if there's no match, because the node or it's parents don't exist, I'd like to be able to easily auto-generate the node and any parent maps & arrays to match the jsonpath expression.
    opened by karlkfi 2
  • Add type math.priority

    Add type math.priority

    Switch to typed consts

    
    type OperationPriority uint8
    
    const (
    	PriorityUnknown OperationPriority = iota
    	PriorityOr
    	PriorityAnd
    	PriorityEq
    	PrioritySum
    	PriorityBit
    	PriorityMul
    	PriorityPow
    )
    
    	priority = map[string]OperationPriority{
    		"**": PriorityPow, // additional: power
    		"*":  PriorityMul,
    		"/":  PriorityMul,
    		"%":  PriorityMul,
    
    good first issue 
    opened by spyzhov 0
  • Add filter for values

    Add filter for values

    Hello! It would be great if there was a possibility of filtering objects by value as it's for arrays values. F.e.:

    1. Array values Here is the data: {"data": ["b"]} And the filter: $.data[?(@ == ‘b’)] will bring the node. Works with arrays fine!

    2. Object values Here is the data: { "price": 1} And I'd like to have a filter like this: $.price?(@ == 1) which would return the node

    opened by voltento 6
Releases(v0.7.1)
Owner
Stepan Pyzhov
Stepan Pyzhov
JSON diff library for Go based on RFC6902 (JSON Patch)

jsondiff jsondiff is a Go package for computing the diff between two JSON documents as a series of RFC6902 (JSON Patch) operations, which is particula

William Poussier 166 May 7, 2022
Fast JSON encoder/decoder compatible with encoding/json for Go

Fast JSON encoder/decoder compatible with encoding/json for Go

Masaaki Goshima 1.5k May 14, 2022
Package json implements encoding and decoding of JSON as defined in RFC 7159

Package json implements encoding and decoding of JSON as defined in RFC 7159. The mapping between JSON and Go values is described in the documentation for the Marshal and Unmarshal functions

High Performance, Kubernetes Native Object Storage 3 May 10, 2022
Json-go - CLI to convert JSON to go and vice versa

Json To Go Struct CLI Install Go version 1.17 go install github.com/samit22/js

Samit Ghimire 5 Mar 3, 2022
JSON Spanner - A Go package that provides a fast and simple way to filter or transform a json document

JSON SPANNER JSON Spanner is a Go package that provides a fast and simple way to

null 2 May 20, 2022
this is a funny client for jsonrpc-client. it can support timeout,breaker ...

this is a funny client for jsonrpc-client. it can support timeout,breaker ...

Lion 21 Sep 26, 2021
JSON query in Golang

gojq JSON query in Golang. Install go get -u github.com/elgs/gojq This library serves three purposes: makes parsing JSON configuration file much easie

Qian Chen 182 Apr 27, 2022
Automatically generate Go (golang) struct definitions from example JSON

gojson gojson generates go struct definitions from json or yaml documents. Example $ curl -s https://api.github.com/repos/chimeracoder/gojson | gojson

Aditya Mukerjee 2.5k May 17, 2022
Arbitrary transformations of JSON in Golang

kazaam Description Kazaam was created with the goal of supporting easy and fast transformations of JSON data with Golang. This functionality provides

Qntfy 226 May 8, 2022
Parsing JSON is a hassle in golang

GoJSON Parsing JSON is a hassle in golang. This package will allow you to parse and search elements in a json without structs. Install gojson go get g

swaraj18 25 Nov 12, 2021
Fast JSON serializer for golang.

easyjson Package easyjson provides a fast and easy way to marshal/unmarshal Go structs to/from JSON without the use of reflection. In performance test

Free and open source software developed at Mail.Ru 3.7k May 22, 2022
Fastest JSON interperter for golang

Welcome To JIN "Your wish is my command" Fast and Easy Way to Deal With JSON Jin is a comprehensive JSON manipulation tool bundle. All functions teste

eco 57 May 14, 2022
Fast Color JSON Marshaller + Pretty Printer for Golang

ColorJSON: The Fast Color JSON Marshaller for Go What is this? This package is based heavily on hokaccha/go-prettyjson but has some noticible differen

Tyler Brock 107 May 17, 2022
Golang port of simdjson: parsing gigabytes of JSON per second

This is a Golang port of simdjson, a high performance JSON parser developed by Daniel Lemire and Geoff Langdale. It makes extensive use of SIMD instructions to achieve parsing performance of gigabytes of JSON per second.

High Performance, Kubernetes Native Object Storage 1.3k May 14, 2022
Kazaam was created with the goal of supporting easy and fast transformations of JSON data with Golang

kazaam Description Kazaam was created with the goal of supporting easy and fast transformations of JSON data with Golang. This functionality provides

Qntfy 205 Sep 17, 2021
Copy of Golang's json library with IsZero feature

json Copy of Golang's json library with IsZero feature from CL13977 Disclaimer It is a package primary used for my own projects, I will keep it up-to-

Ferenc Fabian 4 Oct 9, 2021
Golang JSON decoder supporting case-sensitive, number-preserving, and strict decoding use cases

Golang JSON decoder supporting case-sensitive, number-preserving, and strict decoding use cases

Kubernetes SIGs 12 Apr 3, 2022
Benchmark of Golang JSON Libraries

Introduction This is a benchmark for the json packages. You are welcome to open an issue if you find anything wrong or out of date. TL;DR In conclusio

null 6 Nov 15, 2021
Senml-go - a Golang module for the JSON-based SenML sensor data format

ThingWave SenML module for Golang This is a Golang module for the JSON-based Sen

ThingWave 0 Jan 2, 2022