Skip to content

Commit 7533c29

Browse files
Merge pull request #12 from evilmonkeyinc/feat/v2_expressions
Improved expression
2 parents 69298a0 + 096d783 commit 7533c29

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+3993
-1633
lines changed

README.md

+10-10
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ import (
2525
)
2626

2727
func main() {
28-
query := os.Args[1]
28+
selector := os.Args[1]
2929
data := os.Args[2]
3030

31-
result, err := jsonpath.QueryString(query, data)
31+
result, err := jsonpath.QueryString(selector, data)
3232
if err != nil {
3333
fmt.Println(err)
3434
os.Exit(1)
@@ -44,31 +44,31 @@ The following functions are exported to support the functionality
4444

4545
### Compile
4646

47-
Will parse a JSONPath query and return a JSONPath object that can be used to query multiple JSON data objects or strings
47+
Will parse a JSONPath selector and return a Selector object that can be used to query multiple JSON data objects or strings
4848

4949
### Query
5050

51-
Will compile a JSONPath query and will query the supplied JSON data in any various formats.
51+
Will compile a JSONPath selector and will query the supplied JSON data in any various formats.
5252

5353
The parser can support querying struct types, and will use the `json` tags for struct fields if they are present, if not it will use the names as they appear in the golang code.
5454

5555
### QueryString
5656

57-
Will compile a JSONPath query and will query the supplied JSON data.
57+
Will compile a JSONPath selector and will query the supplied JSON data.
5858

5959
QueryString can support a JSON array or object strings, and will unmarshal them to `[]interface{}` or `map[string]interface{}` using the standard `encoding/json` package unmarshal functions.
6060

6161
## Types
6262

63-
### JSONPath
63+
### Selector
6464

6565
This object is returned by the `Compile` function.
6666

67-
The JSONPath struct represents a reusable compiled JSONPath query which supports the `Query`, and `QueryString` functions as detailed above.
67+
The Selector struct represents a reusable compiled JSONPath selector which supports the `Query`, and `QueryString` functions as detailed above.
6868

6969
### Options
7070

71-
Part of the JSONPath object, Options allows you to specify what additional functionality, if any, that you want to enable while querying data.
71+
Part of the Selector object, Options allows you to specify what additional functionality, if any, that you want to enable while querying data.
7272

7373
You are able to enable index referencing support for maps for all tokens using `AllowMapReferenceByIndex` or use enable it for each token type individually.
7474

@@ -95,7 +95,7 @@ You are able to enable index referencing support for strings for all tokens usin
9595

9696
represents the data object being queried
9797

98-
this should always be the first token in a query. It is also possible to use the root symbol in scripts and filters, for example `$.store.book[?(@.category == $.onSaleCategory)]` would allow you to filter the elements i the book array based on its `category` value compared to the `onSaleCategory` value on the root object.
98+
this should always be the first token in a selector. It is also possible to use the root symbol in scripts and filters, for example `$.store.book[?(@.category == $.onSaleCategory)]` would allow you to filter the elements i the book array based on its `category` value compared to the `onSaleCategory` value on the root object.
9999

100100
### Child
101101

@@ -190,7 +190,7 @@ If used with a map that has a key `length` it will return the corresponding valu
190190

191191
### Subscript, Union, and Range with maps and strings
192192

193-
Using the Compile() function, and modifying the JSONPath Options, it is possible to use a map or a string in place of an array with the subscript `[1]` union `[1,2,3]` and range `[0:3]` operations.
193+
Using the Compile() function, and modifying the Selector Options, it is possible to use a map or a string in place of an array with the subscript `[1]` union `[1,2,3]` and range `[0:3]` operations.
194194

195195
For maps, the keys will be sorted into alphabetical order and they will be used to determine the index order. For example, if you had a map with strings `a` and `b`, regardless of the order, `a` would be the `0` index, and `b` the `1` index.
196196

errors.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ func getInvalidJSONData(reason error) error {
1515
return fmt.Errorf("%w. %s", errors.ErrInvalidJSONData, reason.Error())
1616
}
1717

18-
func getInvalidJSONPathQuery(query string) error {
19-
return fmt.Errorf("%w '%s'", errors.ErrInvalidJSONPathQuery, query)
18+
func getInvalidJSONPathSelector(selector string) error {
19+
return fmt.Errorf("%w '%s'", errors.ErrInvalidJSONPathSelector, selector)
2020
}
2121

22-
func getInvalidJSONPathQueryWithReason(query string, reason error) error {
23-
if goErr.Is(reason, errors.ErrInvalidJSONPathQuery) {
22+
func getInvalidJSONPathSelectorWithReason(selector string, reason error) error {
23+
if goErr.Is(reason, errors.ErrInvalidJSONPathSelector) {
2424
return reason
2525
}
26-
return fmt.Errorf("%w '%s' %s", errors.ErrInvalidJSONPathQuery, query, reason.Error())
26+
return fmt.Errorf("%w '%s' %s", errors.ErrInvalidJSONPathSelector, selector, reason.Error())
2727
}

errors/errors.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import (
77
var (
88
// ErrInvalidExpression returned when an expression is invalid
99
ErrInvalidExpression error = fmt.Errorf("invalid expression")
10-
// ErrInvalidJSONPathQuery returned when the JSONPath query is invalid
11-
ErrInvalidJSONPathQuery error = fmt.Errorf("invalid JSONPath query")
10+
// ErrInvalidJSONPathSelector returned when the JSONPath selector is invalid
11+
ErrInvalidJSONPathSelector error = fmt.Errorf("invalid JSONPath selector")
1212
// ErrInvalidJSONData returned when the JSON data is invalid
1313
ErrInvalidJSONData error = fmt.Errorf("invalid data")
1414
// ErrInvalidToken returned when a token is invalid

errors_test.go

+20-20
Original file line numberDiff line numberDiff line change
@@ -35,40 +35,40 @@ func Test_getInvalidJSONData(t *testing.T) {
3535
}
3636
}
3737

38-
func Test_getInvalidJSONPathQuery(t *testing.T) {
38+
func Test_getInvalidJSONPathSelector(t *testing.T) {
3939

4040
tests := []struct {
4141
input string
4242
expected string
4343
}{
4444
{
4545
input: "",
46-
expected: "invalid JSONPath query ''",
46+
expected: "invalid JSONPath selector ''",
4747
},
4848
{
4949
input: "test",
50-
expected: "invalid JSONPath query 'test'",
50+
expected: "invalid JSONPath selector 'test'",
5151
},
5252
{
5353
input: "other",
54-
expected: "invalid JSONPath query 'other'",
54+
expected: "invalid JSONPath selector 'other'",
5555
},
5656
}
5757

5858
for idx, test := range tests {
5959
t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) {
60-
actual := getInvalidJSONPathQuery(test.input)
60+
actual := getInvalidJSONPathSelector(test.input)
6161
assert.EqualError(t, actual, test.expected)
62-
assert.True(t, goErr.Is(actual, errors.ErrInvalidJSONPathQuery))
62+
assert.True(t, goErr.Is(actual, errors.ErrInvalidJSONPathSelector))
6363
})
6464
}
6565
}
6666

67-
func Test_getInvalidJSONPathQueryWithReason(t *testing.T) {
67+
func Test_getInvalidJSONPathSelectorWithReason(t *testing.T) {
6868

6969
type input struct {
70-
query string
71-
reason error
70+
selector string
71+
reason error
7272
}
7373

7474
tests := []struct {
@@ -77,32 +77,32 @@ func Test_getInvalidJSONPathQueryWithReason(t *testing.T) {
7777
}{
7878
{
7979
input: input{
80-
query: "",
81-
reason: fmt.Errorf("the reason"),
80+
selector: "",
81+
reason: fmt.Errorf("the reason"),
8282
},
83-
expected: "invalid JSONPath query '' the reason",
83+
expected: "invalid JSONPath selector '' the reason",
8484
},
8585
{
8686
input: input{
87-
query: "test",
88-
reason: fmt.Errorf("other reason"),
87+
selector: "test",
88+
reason: fmt.Errorf("other reason"),
8989
},
90-
expected: "invalid JSONPath query 'test' other reason",
90+
expected: "invalid JSONPath selector 'test' other reason",
9191
},
9292
{
9393
input: input{
94-
query: "other",
95-
reason: getInvalidJSONPathQuery("inside"),
94+
selector: "other",
95+
reason: getInvalidJSONPathSelector("inside"),
9696
},
97-
expected: "invalid JSONPath query 'inside'",
97+
expected: "invalid JSONPath selector 'inside'",
9898
},
9999
}
100100

101101
for idx, test := range tests {
102102
t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) {
103-
actual := getInvalidJSONPathQueryWithReason(test.input.query, test.input.reason)
103+
actual := getInvalidJSONPathSelectorWithReason(test.input.selector, test.input.reason)
104104
assert.EqualError(t, actual, test.expected)
105-
assert.True(t, goErr.Is(actual, errors.ErrInvalidJSONPathQuery))
105+
assert.True(t, goErr.Is(actual, errors.ErrInvalidJSONPathSelector))
106106
})
107107
}
108108
}

jsonpath.go

+40-27
Original file line numberDiff line numberDiff line change
@@ -6,65 +6,78 @@ import (
66
"strconv"
77
"strings"
88

9+
"github.com/evilmonkeyinc/jsonpath/option"
10+
"github.com/evilmonkeyinc/jsonpath/script"
11+
"github.com/evilmonkeyinc/jsonpath/script/standard"
912
"github.com/evilmonkeyinc/jsonpath/token"
1013
)
1114

12-
// Compile compile the JSON path query
13-
func Compile(queryPath string) (*JSONPath, error) {
14-
jsonPath := &JSONPath{}
15-
if err := jsonPath.compile(queryPath); err != nil {
16-
return nil, getInvalidJSONPathQueryWithReason(queryPath, err)
15+
// Compile will compile the JSONPath selector
16+
func Compile(selector string) (*Selector, error) {
17+
engine := new(standard.ScriptEngine)
18+
19+
if selector == "$[?(@.key<3),?(@.key>6)]" {
20+
// TODO
21+
selector = "$[?(@.key<3),?(@.key>6)]"
22+
}
23+
24+
jsonPath := &Selector{}
25+
if err := jsonPath.compile(selector, engine); err != nil {
26+
return nil, getInvalidJSONPathSelectorWithReason(selector, err)
1727
}
1828

1929
return jsonPath, nil
2030
}
2131

22-
// Query will return the result of the JSONPath query applied against the specified JSON data.
23-
func Query(queryPath string, jsonData interface{}) (interface{}, error) {
24-
jsonPath, err := Compile(queryPath)
32+
// Query will return the result of the JSONPath selector applied against the specified JSON data.
33+
func Query(selector string, jsonData interface{}) (interface{}, error) {
34+
jsonPath, err := Compile(selector)
2535
if err != nil {
26-
return nil, getInvalidJSONPathQueryWithReason(queryPath, err)
36+
return nil, getInvalidJSONPathSelectorWithReason(selector, err)
2737
}
2838
return jsonPath.Query(jsonData)
2939
}
3040

31-
// QueryString will return the result of the JSONPath query applied against the specified JSON data.
32-
func QueryString(queryPath string, jsonData string) (interface{}, error) {
33-
jsonPath, err := Compile(queryPath)
41+
// QueryString will return the result of the JSONPath selector applied against the specified JSON data.
42+
func QueryString(selector string, jsonData string) (interface{}, error) {
43+
44+
jsonPath, err := Compile(selector)
3445
if err != nil {
35-
return nil, getInvalidJSONPathQueryWithReason(queryPath, err)
46+
return nil, getInvalidJSONPathSelectorWithReason(selector, err)
3647
}
3748
return jsonPath.QueryString(jsonData)
3849
}
3950

40-
// JSONPath represents a compiled JSONPath query
51+
// Selector represents a compiled JSONPath selector
4152
// and exposes functions to query JSON data and objects.
42-
type JSONPath struct {
43-
Options *token.Options
44-
queryString string
45-
tokens []token.Token
53+
type Selector struct {
54+
Options *option.QueryOptions
55+
engine script.Engine
56+
tokens []token.Token
57+
selector string
4658
}
4759

48-
// String returns the compiled query string representation
49-
func (query *JSONPath) String() string {
60+
// String returns the compiled selector string representation
61+
func (query *Selector) String() string {
5062
jsonPath := ""
5163
for _, token := range query.tokens {
5264
jsonPath += fmt.Sprintf("%s", token)
5365
}
5466
return jsonPath
5567
}
5668

57-
func (query *JSONPath) compile(queryString string) error {
58-
query.queryString = queryString
69+
func (query *Selector) compile(selector string, engine script.Engine) error {
70+
query.engine = engine
71+
query.selector = selector
5972

60-
tokenStrings, err := token.Tokenize(queryString)
73+
tokenStrings, err := token.Tokenize(selector)
6174
if err != nil {
6275
return err
6376
}
6477

6578
tokens := make([]token.Token, len(tokenStrings))
6679
for idx, tokenString := range tokenStrings {
67-
token, err := token.Parse(tokenString, query.Options)
80+
token, err := token.Parse(tokenString, query.engine, query.Options)
6881
if err != nil {
6982
return err
7083
}
@@ -76,9 +89,9 @@ func (query *JSONPath) compile(queryString string) error {
7689
}
7790

7891
// Query will return the result of the JSONPath query applied against the specified JSON data.
79-
func (query *JSONPath) Query(root interface{}) (interface{}, error) {
92+
func (query *Selector) Query(root interface{}) (interface{}, error) {
8093
if len(query.tokens) == 0 {
81-
return nil, getInvalidJSONPathQuery(query.queryString)
94+
return nil, getInvalidJSONPathSelector(query.selector)
8295
}
8396

8497
tokens := make([]token.Token, 0)
@@ -94,7 +107,7 @@ func (query *JSONPath) Query(root interface{}) (interface{}, error) {
94107
}
95108

96109
// QueryString will return the result of the JSONPath query applied against the specified JSON data.
97-
func (query *JSONPath) QueryString(jsonData string) (interface{}, error) {
110+
func (query *Selector) QueryString(jsonData string) (interface{}, error) {
98111
jsonData = strings.TrimSpace(jsonData)
99112
if jsonData == "" {
100113
return nil, getInvalidJSONData(errDataIsUnexpectedTypeOrNil)

0 commit comments

Comments
 (0)