diff --git a/gjson.go b/gjson.go index ec2ce35..e74551f 100644 --- a/gjson.go +++ b/gjson.go @@ -1807,7 +1807,7 @@ func isSimpleName(component string) bool { return false } switch component[i] { - case '[', ']', '{', '}', '(', ')', '#', '|': + case '[', ']', '{', '}', '(', ')', '#', '|', '!': return false } } @@ -1871,23 +1871,25 @@ type parseContext struct { // use the Valid function first. func Get(json, path string) Result { if len(path) > 1 { - if !DisableModifiers { - if path[0] == '@' { - // possible modifier - var ok bool - var npath string - var rjson string + if (path[0] == '@' && !DisableModifiers) || path[0] == '!' { + // possible modifier + var ok bool + var npath string + var rjson string + if path[0] == '@' && !DisableModifiers { npath, rjson, ok = execModifier(json, path) - if ok { - path = npath - if len(path) > 0 && (path[0] == '|' || path[0] == '.') { - res := Get(rjson, path[1:]) - res.Index = 0 - res.Indexes = nil - return res - } - return Parse(rjson) + } else if path[0] == '!' { + npath, rjson, ok = execStatic(json, path) + } + if ok { + path = npath + if len(path) > 0 && (path[0] == '|' || path[0] == '.') { + res := Get(rjson, path[1:]) + res.Index = 0 + res.Indexes = nil + return res } + return Parse(rjson) } } if path[0] == '[' || path[0] == '{' { @@ -2556,8 +2558,40 @@ func safeInt(f float64) (n int64, ok bool) { return int64(f), true } +// execStatic parses the path to find a static value. +// The input expects that the path already starts with a '!' +func execStatic(json, path string) (pathOut, res string, ok bool) { + name := path[1:] + if len(name) > 0 { + switch name[0] { + case '{', '[', '"', '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9': + _, res = parseSquash(name, 0) + pathOut = name[len(res):] + return pathOut, res, true + } + } + for i := 1; i < len(path); i++ { + if path[i] == '|' { + pathOut = path[i:] + name = path[1:i] + break + } + if path[i] == '.' { + pathOut = path[i:] + name = path[1:i] + break + } + } + switch strings.ToLower(name) { + case "true", "false", "null", "nan", "inf": + return pathOut, name, true + } + return pathOut, res, false +} + // execModifier parses the path to find a matching modifier function. -// then input expects that the path already starts with a '@' +// The input expects that the path already starts with a '@' func execModifier(json, path string) (pathOut, res string, ok bool) { name := path[1:] var hasArgs bool diff --git a/gjson_test.go b/gjson_test.go index 61cefdf..50760c2 100644 --- a/gjson_test.go +++ b/gjson_test.go @@ -2410,3 +2410,31 @@ func TestQueryGetPath(t *testing.T) { assert(t, arr[i].Path(readmeJSON) == fmt.Sprintf("friends.%d.first", i)) } } + +func TestStaticJSON(t *testing.T) { + json := `{ + "name": {"first": "Tom", "last": "Anderson"} + }` + assert(t, Get(json, + `"bar"`).Raw == + ``) + assert(t, Get(json, + `!"bar"`).Raw == + `"bar"`) + assert(t, Get(json, + `!{"name":{"first":"Tom"}}.{name.first}.first`).Raw == + `"Tom"`) + assert(t, Get(json, + `{name.last,"foo":!"bar"}`).Raw == + `{"last":"Anderson","foo":"bar"}`) + assert(t, Get(json, + `{name.last,"foo":!{"a":"b"},"that"}`).Raw == + `{"last":"Anderson","foo":{"a":"b"}}`) + assert(t, Get(json, + `{name.last,"foo":!{"c":"d"},!"that"}`).Raw == + `{"last":"Anderson","foo":{"c":"d"},"_":"that"}`) + assert(t, Get(json, + `[!true,!false,!null,!inf,!nan,!hello,{"name":!"andy",name.last},+inf,!["any","thing"]]`).Raw == + `[true,false,null,inf,nan,{"name":"andy","last":"Anderson"},["any","thing"]]`, + ) +}