Coder Social home page Coder Social logo

go-jmespath's Introduction

JMESPath

JMESPath (pronounced "james path") allows you to declaratively specify how to extract elements from a JSON document.

For example, given this document:

{"foo": {"bar": "baz"}}

The jmespath expression foo.bar will return "baz".

JMESPath also supports:

Referencing elements in a list. Given the data:

{"foo": {"bar": ["one", "two"]}}

The expression: foo.bar[0] will return "one". You can also reference all the items in a list using the * syntax:

{"foo": {"bar": [{"name": "one"}, {"name": "two"}]}}

The expression: foo.bar[*].name will return ["one", "two"]. Negative indexing is also supported (-1 refers to the last element in the list). Given the data above, the expression foo.bar[-1].name will return "two".

The * can also be used for hash types:

{"foo": {"bar": {"name": "one"}, "baz": {"name": "two"}}}

The expression: foo.*.name will return ["one", "two"].

Installation

You can install JMESPath from pypi with:

pip install jmespath

API

The jmespath.py library has two functions that operate on python data structures. You can use search and give it the jmespath expression and the data:

>>> import jmespath
>>> path = jmespath.search('foo.bar', {'foo': {'bar': 'baz'}})
'baz'

Similar to the re module, you can use the compile function to compile the JMESPath expression and use this parsed expression to perform repeated searches:

>>> import jmespath
>>> expression = jmespath.compile('foo.bar')
>>> expression.search({'foo': {'bar': 'baz'}})
'baz'
>>> expression.search({'foo': {'bar': 'other'}})
'other'

This is useful if you're going to use the same jmespath expression to search multiple documents. This avoids having to reparse the JMESPath expression each time you search a new document.

Options

You can provide an instance of jmespath.Options to control how a JMESPath expression is evaluated. The most common scenario for using an Options instance is if you want to have ordered output of your dict keys. To do this you can use either of these options:

>>> import jmespath
>>> jmespath.search('{a: a, b: b}',
...                 mydata,
...                 jmespath.Options(dict_cls=collections.OrderedDict))


>>> import jmespath
>>> parsed = jmespath.compile('{a: a, b: b}')
>>> parsed.search(mydata,
...               jmespath.Options(dict_cls=collections.OrderedDict))

Custom Functions

The JMESPath language has numerous built-in functions, but it is also possible to add your own custom functions. Keep in mind that custom function support in jmespath.py is experimental and the API may change based on feedback.

If you have a custom function that you've found useful, consider submitting it to jmespath.site and propose that it be added to the JMESPath language. You can submit proposals here.

To create custom functions:

  • Create a subclass of jmespath.functions.Functions.
  • Create a method with the name _func_<your function name>.
  • Apply the jmespath.functions.signature decorator that indicates the expected types of the function arguments.
  • Provide an instance of your subclass in a jmespath.Options object.

Below are a few examples:

import jmespath
from jmespath import functions

# 1. Create a subclass of functions.Functions.
#    The function.Functions base class has logic
#    that introspects all of its methods and automatically
#    registers your custom functions in its function table.
class CustomFunctions(functions.Functions):

    # 2 and 3.  Create a function that starts with _func_
    # and decorate it with @signature which indicates its
    # expected types.
    # In this example, we're creating a jmespath function
    # called "unique_letters" that accepts a single argument
    # with an expected type "string".
    @functions.signature({'types': ['string']})
    def _func_unique_letters(self, s):
        # Given a string s, return a sorted
        # string of unique letters: 'ccbbadd' ->  'abcd'
        return ''.join(sorted(set(s)))

    # Here's another example.  This is creating
    # a jmespath function called "my_add" that expects
    # two arguments, both of which should be of type number.
    @functions.signature({'types': ['number']}, {'types': ['number']})
    def _func_my_add(self, x, y):
        return x + y

# 4. Provide an instance of your subclass in a Options object.
options = jmespath.Options(custom_functions=CustomFunctions())

# Provide this value to jmespath.search:
# This will print 3
print(
    jmespath.search(
        'my_add(`1`, `2`)', {}, options=options)
)

# This will print "abcd"
print(
    jmespath.search(
        'foo.bar | unique_letters(@)',
        {'foo': {'bar': 'ccbbadd'}},
        options=options)
)

Again, if you come up with useful functions that you think make sense in the JMESPath language (and make sense to implement in all JMESPath libraries, not just python), please let us know at jmespath.site.

Specification

If you'd like to learn more about the JMESPath language, you can check out the JMESPath tutorial. Also check out the JMESPath examples page for examples of more complex jmespath queries.

The grammar is specified using ABNF, as described in RFC4234. You can find the most up to date grammar for JMESPath here.

You can read the full JMESPath specification here.

Testing

In addition to the unit tests for the jmespath modules, there is a tests/compliance directory that contains .json files with test cases. This allows other implementations to verify they are producing the correct output. Each json file is grouped by feature.

Discuss

Join us on our Gitter channel if you want to chat or if you have any questions.

go-jmespath's People

Contributors

ajorg-aws avatar brianbland avatar clinty avatar ejholmes avatar eparis avatar highlyunavailable avatar jamesls avatar jasdel avatar reedobrien avatar shawnps avatar shawnps-sigsci avatar willabides avatar xibz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-jmespath's Issues

Filtering objects by date

How do you filter by time.Time? I was trying to filter Amazon S3 Objects from the list api and I can't get it to work in go, but I can get it to work in jmespath.org.

This seems to work in jmespath.org but not in go-jmespath
[?LastModified>'2023-01-23T00:00:00Z' && LastModified<'2023-04-01T00:00:00Z']

Here is an example

[
  {
    "ChecksumAlgorithm": null,
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "Key": "images-demo/",
    "LastModified": "2023-03-23T21:07:02Z",
    "Owner": null,
    "Size": 0,
    "StorageClass": "STANDARD"
  },
  {
    "ChecksumAlgorithm": null,
    "ETag": "\"8fe2b567e7ded83cbf12c76104da11b4\"",
    "Key": "images-demo/1.jpg",
    "LastModified": "2023-03-23T21:07:21Z",
    "Owner": null,
    "Size": 5104348,
    "StorageClass": "STANDARD"
  },
  {
    "ChecksumAlgorithm": null,
    "ETag": "\"0fba471e21efe99aba7a9caf20cab050\"",
    "Key": "images-demo/7.jpg",
    "LastModified": "2023-04-19T22:26:08Z",
    "Owner": null,
    "Size": 207020,
    "StorageClass": "STANDARD"
  }
]

json.Number format is not supported

If json is Unmarshaled using json.Number format like:

my_map := make(map[string]interface{})
d := json.NewDecoder(strings.NewReader(json_raw))
d.UseNumber()
d.Decode(&my_map)

And then used to search:

var expr1 = jmespath.MustCompile("people[?age > `20`].[name, age]")
result, err := expr1.Search(my_map)

err is nil but the result is [] even though some objects must be returned. Removing the d.UseNumber() makes the Search work properly.

This is probably because this type assertion fails to recognize the value in the map as a Number (float): https://github.com/jmespath/go-jmespath/blob/master/interpreter.go#L48

json.Number is essentially a string. So either reflect/conversion library can be used here or type assertion should handle json.Number.

Probable bug: mysterious nulls from wildcard expression

I ran this program:

package main

import (
	"encoding/json"
	"fmt"

	"github.com/jmespath/go-jmespath"
)

func main() {
	jsonBytes := []byte(`
		{
		   "author": "Mark Twain",
		   "title": "The Adventures of Tom Sawyer",
		   "words": 80000,
		   "year": 1876
		}
	`)
	var jsonData any
	err := json.Unmarshal(jsonBytes, &jsonData)
	if err != nil {
		panic(err)
	}

	queryResult, err := jmespath.Search("*.to_string(@)", jsonData)
	if err != nil {
		panic(err)
	}

	fmt.Printf("%#v\n", queryResult)
}

Its output was:

[]interface {}{"null", "null", "null", "null", "Mark Twain", "The Adventures of Tom Sawyer", "80000", "1876"}

I don't understand where the "null" strings are coming from. This behaviour looks like a bug to me.

Here are go.mod and go.sum:

module bug-report

go 1.20

require github.com/jmespath/go-jmespath v0.4.0
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

Comparison with JavaScript

I ran these commands:

npm install [email protected]
./node_modules/jmespath/jp.js '*.to_string(@)' < book.json

Here is book.json:

{
   "author": "Mark Twain",
   "title": "The Adventures of Tom Sawyer",
   "words": 80000,
   "year": 1876
}

The output of the second command was:

["Mark Twain","The Adventures of Tom Sawyer","80000","1876"]

This is the output I expect to see from that JSON and that JMESPath query. It's obviously different from the output of the Go program.

supporting Hyphen"-" in Compile and Search

Hi,
I tried the below code, where we want to search jmes path having Hyphen "-", I'm getting the following error
SyntaxError: Unexpected token at the end of the expression: tNumber

From the code it looks like the Hyphen "-" is considered as a reference to get numeric values.

go-jmespath/lexer.go

Lines 164 to 166 in 3d4fd11

} else if r == '-' || (r >= '0' && r <= '9') {
t := lexer.consumeNumber()
tokens = append(tokens, t)

package main

import (
	"encoding/json"
	"fmt"
	"github.com/jmespath/go-jmespath"
)

func main() {
	var jsondata = []byte(`{"foo-bar": {"bar": "car"}}`)
	var data interface{}
	err := json.Unmarshal(jsondata, &data)
	fmt.Println(err)
	precompiled, err := jmespath.Compile("foo-bar")
	if err != nil{
      	    fmt.Println(err)
   	}
    	result, err := precompiled.Search(data)
	fmt.Printf("%T", result)
	fmt.Println(result, err)
}

Is it possible to pass values containing Hyphen "-"? jmespath.Compile("foo-bar")

Please let us know if there is any other way to do that, If not please consider our suggestion.
Opinion:-

Can we add a filter func in Lexer

go-jmespath/lexer.go

Lines 23 to 29 in 3d4fd11

// Lexer contains information about the expression being tokenized.
type Lexer struct {
expression string // The expression provided by the user.
currentPos int // The current position in the string.
lastWidth int // The width of the current rune. This
buf bytes.Buffer // Internal buffer used for building up values.
}

type SkipFilterFunc func(r rune) bool

// Lexer contains information about the expression being tokenized.
type Lexer struct {
	expression string       // The expression provided by the user.
	currentPos int          // The current position in the string.
	lastWidth  int          // The width of the current rune.  This
	buf        bytes.Buffer // Internal buffer used for building up values.
	skipFunc []SkipFilterFunc 
}

go-jmespath/lexer.go

Lines 388 to 392 in 3d4fd11

r := lexer.next()
if r < 0 || r > 128 || identifierTrailingBits[uint64(r)/64]&(1<<(uint64(r)%64)) == 0 {
lexer.back()
break
}

And we can call those filter func inside consuleUnquotedIdentifier

		if r < 0 || r > 128 || identifierTrailingBits[uint64(r)/64]&(1<<(uint64(r)%64)) == 0 {
			skip := false
			for _, skipFunc := range lexer.skipFunc{
				if ok := skipFunc(r); ok {
					skip = true
					break
				}
			}
			if skip{
				continue
			}
			lexer.back()
			break

		}

So I can support any key to be part of string and can override the existing feature.

[question] examples of usage?

Hi, I mostly wonder what kind of data jmespath.Search() expects?
Param data says interface{} so it's not really helpful... can I use string {} or []byte("{}") or what it should be?
From testcases I see that map[string]interface{} is used so it looks like output of json.Unmarshal... but is this the only data type that can be passed? Or maybe json string is allowed too?

Same questions apply to return value... what kind of data types I can expect? interface{} type suggest I can expect various data types, or maybe even self defined ones... but there is no info about it... so no idea what should I try to assert.

I was trying to do something simple like:

v, err := jmespath.Search("a.b", `{"a":{"b":"test"}}`)
log.Println(v)

but this doesn't seem to work

Also, considering jmespath.Search() returns interface{} then beside type assertion is there any nicer way to convert this some usable data type? Like string or []byte or self defined struct (e.g. like with json.Unmarshal)?

The order of an object to array conversion seems to be unstable

I have a json alphabetically ordered as input to the Search method. When I use a query which converts an object to an array, the order gets lost. This does not seem to be the case in over implementations of the jmespath specification like the jmespath.js library. The query I am using is the following:

{labels: ['Feels Like', 'Humidity', 'Pressure', 'Temperature', 'Max Temp', 'Min Temp'], datasets: [{label: 'Weather', data: main.*}]}

The output I am getting is the following:

{
	"labels": [
		"Feels Like",
		"Humidity",
		"Pressure",
		"Temperature",
		"Max Temp",
		"Min Temp"
	],
	"datasets": [
		{
			"label": "Weather",
			"data": [
				-2.46,
				86,
				1022,
				1.44,
				3.33,
				0
			]
		}
	]
}

The numbers in the data array change randomly the order, even though the input object is always alphabetically ordered. My Golang code looks like the following:

var d2 interface{}
json.Unmarshal([]byte(tmp), &d2)

log.Printf("before: %#v", d2)
queried, err := jmespath.Search(data.Query, d2)
if err != nil {
	log.Fatal(err)
}

log.Printf("after: %#v", queried)

From the json specification lists have to stay ordered and fields in objects are unordered. So I have this problem with converting an object to an array since I need another array with labels which refers to it in the same order.

Is this an implementation issue? How can I solve this?

Best regards,

Thilo

Following error occurred: invalid character '-' after top-level value

Here is a example of how to reproduce the error:

package main

import (
    "encoding/json"
    "log"

    "github.com/jmespath/go-jmespath"
)

func main() {
    var jsonBlob = []byte(`[
    {
        "oeeId": 3162396,
        "oeeDate": "2019-03-06T00:00:00",
        "oee": 21.2
    }]`)

    var d interface{}

    err := json.Unmarshal(jsonBlob, &d)
    if err != nil {
        log.Printf("1: %s", err)
    }

    res, err := jmespath.Search("{ labels: ['example'], datasets: [{label: 'Verfügbarkeit', data: [?oeeDate == `2019-03-06T00:00:00`].oee}]}", d)
    if err != nil {
        log.Printf("2: %s", err)
    }
    log.Println(res)
}

When I execute this code I get the following error:

invalid character '-' after top-level value

I am wondering what's my issue with this code since this example works with other Jmespath implementations like the javascript jmespath.js library.

The error seems to be in the query rather than the input data. Can anybody help me on this one?

Does not support struct tags

I have a series of structs to represent my JSON, which I use instead of a generic interface{}. My structs have json: tags, but JMESPath appears to ignore them.

Complex expression with struct produces error

I've noticed there is a error searching with struct using a complex expression, where as the map[string]interface{} form parses correctly using jpgo.

In aws/aws-sdk-go#457 we discovered an expression and input which searches correctly with jpgo, but fails if the same input values are applied to our ECS.DescribeServicesOutput struct.

The expression being used is:

services | [@[?length(deployments)!=`1`], @[?desiredCount!=runningCount]][] | length(@) == `0`

The error returned from jsmespath.Search is:

Invalid type for: <nil>, expected: []jmespath.jpType{"string", "array", "object"` ...

This comment has a example JSON input which is marshaled to our ECS.DescribeServicesOutput struct.

Can nested JSON string parsing be supported?

Can nested JSON string parsing be supported?
example:

var data = `{
    "name": "John",
    "nested_json": "{\"age\": 25, \"city\": \"New York\"}"
}`

expression = jmespath.compile('nested_json | from_json_string(@).[age,city]')
// from_json_string(nested_json).[age,city]

var result = expression.search(data)

output:
["25","New York"]

fails compliance tests for benchmarks, functions, literal, and syntax.

Go version of jmespath is failing compliance tests in the following suites: benchmarks, functions, literal, and syntax.

The python version fails only in benchmarks suite.

% jp-compliance -e jp -t benchmarks
'error'

% jp-compliance -e jp -t functions
.................
FAIL functions,0,17
The expression: avg(empty_list)
was suppose to give: null
for the JSON: {"foo": -1, "zero": 0, "numbers": [-1, 3, 4, 5], "array": [-1, 3, 4, 5, "a", "100"], "strings": ["a", "b", "c"], "decimals": [1.01, 1.2, -1.5], "str": "Str", "false": false, "empty_list": [], "empty_hash": {}, "objects": {"foo": "bar", "bar": "baz"}, "null_key": null}
but instead gave: ""
..........................................................................................................................................................
FAIL

% jp-compliance -e jp -t literal
........................................
FAIL literal,2,12
The expression: '\\'
was suppose to give: "\\\\"
for the JSON: {}
but instead gave: ""
FAIL

% jp-compliance -e jp -t syntax
...............................................
FAIL syntax,8,1
The expression: [:::]
was suppose to have non zero for error error: syntax
but instead gave rc of: 0, stderr: 
.....................................................................................
OK

PR 15 introduced unintended pointer vs value return changes

In #15 it looks like some unintended changes where introduced. I scalar types are not being returned as values types even if the field for that value was a pointer type. Structs still are being returned as pointer types if they originally were.

This issue was reported to us in aws/aws-sdk-go#535 as it is breaking some functionality for those not using vendoring with the SDK.

Comparison expressions don't evaluate operand type

Description

Comparison expressions do not evaluate as expected in cases where one operand is a literal value and the other is a pointer to a value. This results in expressions such as AutoScalingGroups[].[length(Instances[?LifecycleState=='InService'] used in the AWS SDK for Go's Autoscaling waiter WaitUntilGroupInService not evaluating correctly since LifecycleState is a pointer to a string that is being evaluated against the literal string InService.

Steps to reproduce

Save this code as main_test.go and run go test -v:

package main

import (
    "testing"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/awsutil"
    "github.com/aws/aws-sdk-go/service/autoscaling"
)

func TestWaitUntilGroupInService(t *testing.T) {
    mockResponse := &autoscaling.DescribeAutoScalingGroupsOutput{
        AutoScalingGroups: []*autoscaling.Group{
            &autoscaling.Group{
                Instances: []*autoscaling.Instance{
                    &autoscaling.Instance{
                        LifecycleState: aws.String("InService"),
                    },
                    &autoscaling.Instance{
                        LifecycleState: aws.String("InService"),
                    },
                },
                MaxSize:         aws.Int64(4),
                MinSize:         aws.Int64(2),
                DesiredCapacity: aws.Int64(2),
            },
            &autoscaling.Group{
                Instances: []*autoscaling.Instance{
                    &autoscaling.Instance{
                        LifecycleState: aws.String("InService"),
                    },
                    &autoscaling.Instance{
                        LifecycleState: aws.String("Pending"),
                    },
                },
                MaxSize:         aws.Int64(4),
                MinSize:         aws.Int64(2),
                DesiredCapacity: aws.Int64(2),
            },
        },
    }

    var testCases = []struct {
        expect []interface{}
        data   interface{}
        path   string
    }{
        {[]interface{}{true}, mockResponse, "contains(AutoScalingGroups[].[length(Instances[?LifecycleState=='InService']) >= MinSize][], `false`)"},
        {[]interface{}{true, false}, mockResponse, "AutoScalingGroups[].[length(Instances[?LifecycleState=='InService']) >= MinSize][]"},
        {[]interface{}{2, 1}, mockResponse, "AutoScalingGroups[].[length(Instances[?LifecycleState=='InService'])][]"},
    }
    for i, c := range testCases {
        v, err := awsutil.ValuesAtPath(c.data, c.path)
        if err != nil {
            t.Errorf("case %v, expected no error, %v", i, c.path)
        }
        if e, a := c.expect, v; !awsutil.DeepEqual(e, a) {

            t.Errorf("case %v, %v, expected: %#v, but got: %#v", i, c.path, e, a)
        }
    }
}
$ go get -u github.com/aws/aws-sdk-go

$ go test -v
=== RUN   TestWaitUntilGroupInService
--- FAIL: TestWaitUntilGroupInService (0.00s)
    main_test.go:59: case 0, contains(AutoScalingGroups[].[length(Instances[?LifecycleState=='InService']) >= MinSize][], `false`), expected: []interface {}{true}, but got: []interface {}{false}
    main_test.go:59: case 1, AutoScalingGroups[].[length(Instances[?LifecycleState=='InService']) >= MinSize][], expected: []interface {}{true, false}, but got: []interface {}{}
    main_test.go:59: case 2, AutoScalingGroups[].[length(Instances[?LifecycleState=='InService'])][], expected: []interface {}{2, 1}, but got: []interface {}{0, 0}
FAIL
exit status 1
FAIL    _/Users/masayuki-morita/work/tmp/20190228       0.019s

Related items

Originating issue: aws/aws-sdk-go#2478
Related to #15 and #20.

Return reference for modification

Is it possible to locate a node in the JSON structure and return it for modification, then use json.Marshall to write the modified JSON struct?

Expose treeInterpreter.Execute

I'm building something that seems like it would be a good fit for JMES, but where JMES paths are pre-configured and then used to filter multiple (thousands of) distinct JSON objects of the same JSON "schema" that come in over time - e.g. journalctl output. To avoid re-lexing and re-parsing the path every single operation (which won't be changing), it would be nice to "compile" the AST from a config file's JMES path, then just send it to treeInterpreter.Execute over and over.

I'd be happy to do the work (probably exposing it as jmespath.SearchParsed(expression *ASTNode, data interface{}) (interface{}, error) or something similar) if a pull request would be accepted.

is this repo being maintained?

A simple valid looking pull request open for months, two issues unanswered for months, no commit for months... Is this repo being maintained or is it abandon-ware?

Audit existing jmespath functions for reflection usage

We need to avoid checks for empty interfaces and map[string]interface{} and instead use reflection. This has been fixed in interpreter.go but functions.go needs the same treatment.

I'd also like to investigate adding autogen'd tests using structs from the compliance tests to verify we don't miss anything.

Related: #14

Does not support keys with colon (:) characters

Given following JSON:

{
  "custom:groups": [
    "foo",
    "bar"
  ]
}

And the following JMESPath expression:
contains(custom:groups[*], 'foo') && 'Admin' || 'Editor'

Expected expression to evaluate to "Admin".

Also tried the following expressions without success:
contains("custom:groups[*]", 'foo') && 'Admin' || 'Editor'
contains('"custom:groups[*]"', 'foo') && 'Admin' || 'Editor'

Doesn't work at https://jmespath.org/ either, but maybe this use case is not supported?

Ref: grafana/grafana#22986

Filter of comparison works wrong

The following codes run with no item returned:
jsondata := []byte({ "table1": [ { "age": 30, "name": "John" } ] })
var data interface{}
json.Unmarshal(jsondata, &data)
result, err := jmespath.Search("table1[?age > '25']", data)

After reading the go-jmespath codes, i found that in the file "github.com\jmespath\[email protected]\interpreter.go", line 52, need to consider a string to float convertion, otherwise, we cannot get the right search result.

regular expression support

Following up on jmespath/jmespath.jep#23

different programming languages have different regular expression implementations

Fine, but how about providing a regular expressions function among the Built-in Functions.
The regular expressions will be Go specific, but that's much better than not having any regular expression support, for jp at least.

Slices are not flattened.

The following test case shows the expression search not flattening the passed in struct.

func TestCanSupportFlattenNestedSlice(t *testing.T) {
    assert := assert.New(t)
    data := nestedSlice{A: []sliceType{
        {B: []scalars{{"f1", ""}, {"f1", ""}}},
        {B: []scalars{{"f2", ""}, {"f2", ""}}},
    }}
    result, err := Search("A[].B[]", data)
    assert.Nil(err)
    assert.Equal([]interface{}{"f1", "f1", "f2", "f2"}, result)
}

Fix SNYK-GOLANG-GOPKGINYAMLV3-2841557

Hey all - I'm trying to solve https://security.snyk.io/vuln/SNYK-GOLANG-GOPKGINYAMLV3-2841557 which I'm getting via https://github.com/aws/aws-sdk-go. Usually, I'd put a PR in to bump the dependency in the tree but as it seems the link is testify which has been submodule here due to lock testify at 1.5.1 maintaining compatibility with Go <1.12 I'm not 100% on the next steps.

Does anyone with a better understanding of this package have any pointers on how to mitigate this vulnerability?

merge in `go-jmespath` / `jp` panics when parameters are not JSON objects

echo {"message": "foo"} | jp "merge({message: message}, contextMap)"
panic: interface conversion: interface {} is nil, not map[string]interface {}

goroutine 1 [running]:
github.com/jmespath/go-jmespath.jpfMerge({0xc00004ec20, 0x2, 0xc00000e3c0})
        /Users/jamessar/Source/go/pkg/mod/github.com/jmespath/[email protected]/functions.go:520 +0x197
github.com/jmespath/go-jmespath.(*functionCaller).CallFunction(0x70e1c0, {0xc00000e3c0, 0x5}, {0xc00004ec20, 0x2, 0x2}, 0xc000006060)
        /Users/jamessar/Source/go/pkg/mod/github.com/jmespath/[email protected]/functions.go:401 +0x264
github.com/jmespath/go-jmespath.(*treeInterpreter).Execute(0xc000006060, {0x4, {0x707500, 0xc00004cfd0}, {0xc000020420, 0x2, 0x2}}, {0x711c40, 0xc0000769f0})
        /Users/jamessar/Source/go/pkg/mod/github.com/jmespath/[email protected]/interpreter.go:77 +0x1f35
github.com/jmespath/go-jmespath.Search({0xc00000e3c0, 0x25}, {0x711c40, 0xc0000769f0})
        /Users/jamessar/Source/go/pkg/mod/github.com/jmespath/[email protected]/api.go:48 +0x173
main.runMain(0xc000154000)
        /Users/jamessar/Source/jp/jp.go:105 +0x7d4
main.runMainAndExit(0xc000072a00)
        /Users/jamessar/Source/jp/jp.go:51 +0x19
github.com/urfave/cli.HandleAction({0x706800, 0x7482c0}, 0x25)
        /Users/jamessar/Source/go/pkg/mod/github.com/urfave/[email protected]/app.go:526 +0x50
github.com/urfave/cli.(*App).Run(0xc000152000, {0xc00004e3c0, 0x2, 0x2})
        /Users/jamessar/Source/go/pkg/mod/github.com/urfave/[email protected]/app.go:286 +0x625
main.main()
        /Users/jamessar/Source/jp/jp.go:47 +0x4a5

Unable to match data on map[string]struct

I'm having an issue when executing a JMESPATH query on a map[string]customType, here's the code :

type testStruct struct {
	Value interface{} `json:"value"`
}

func main() {
	test1 := map[string]interface{}{
		"entry": testStruct{Value: 1},
	}
	result1, err := jmespath.Search("entry.value", test1)
	fmt.Printf("\nerr = %v : res = %+v\n", err, result1) // displays err = nil : res = 1

	test2 := map[string]testStruct{
		"entry": testStruct{Value: 1},
	}
	result2, err := jmespath.Search("entry.value", test2)
	fmt.Printf("\nerr = %v : res = %+v\n", err, result2) // displays err = nil : res = nil
}

As you can see, a query on a map[string]interface{} behaves as expected, whereas nil is returned when executing the same query on a map[string]testStruct (adding a pointer to testStruct results in the same output).

Has anyone ever experienced such a problem ?

Case-insensitive search with JMESPath

Simple question: How to do a case-insensitive searching with JMESPath?

Let's say to search for foo in this JSON:

[
  "foo",
  "foobar",
  "barfoo",
  "bar",
  "baz",
  "barbaz",
  "FOO"
]

Here is the case-sensitive search query:

[?contains(@, 'foo')]

It will returns ["foo", "foobar", "barfoo"] but it misses "FOO".

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.