123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799 |
- // Package gjson provides searching for json strings.
- package gjson
- import (
- "encoding/base64"
- "encoding/json"
- "errors"
- "reflect"
- "strconv"
- "strings"
- "sync"
- "sync/atomic"
- "time"
- "unicode/utf16"
- "unicode/utf8"
- "github.com/tidwall/match"
- "github.com/tidwall/pretty"
- )
- // Type is Result type
- type Type int
- const (
- // Null is a null json value
- Null Type = iota
- // False is a json false boolean
- False
- // Number is json number
- Number
- // String is a json string
- String
- // True is a json true boolean
- True
- // JSON is a raw block of JSON
- JSON
- )
- // String returns a string representation of the type.
- func (t Type) String() string {
- switch t {
- default:
- return ""
- case Null:
- return "Null"
- case False:
- return "False"
- case Number:
- return "Number"
- case String:
- return "String"
- case True:
- return "True"
- case JSON:
- return "JSON"
- }
- }
- // Result represents a json value that is returned from Get().
- type Result struct {
- // Type is the json type
- Type Type
- // Raw is the raw json
- Raw string
- // Str is the json string
- Str string
- // Num is the json number
- Num float64
- // Index of raw value in original json, zero means index unknown
- Index int
- }
- // String returns a string representation of the value.
- func (t Result) String() string {
- switch t.Type {
- default:
- return ""
- case False:
- return "false"
- case Number:
- if len(t.Raw) == 0 {
- // calculated result
- return strconv.FormatFloat(t.Num, 'f', -1, 64)
- }
- var i int
- if t.Raw[0] == '-' {
- i++
- }
- for ; i < len(t.Raw); i++ {
- if t.Raw[i] < '0' || t.Raw[i] > '9' {
- return strconv.FormatFloat(t.Num, 'f', -1, 64)
- }
- }
- return t.Raw
- case String:
- return t.Str
- case JSON:
- return t.Raw
- case True:
- return "true"
- }
- }
- // Bool returns an boolean representation.
- func (t Result) Bool() bool {
- switch t.Type {
- default:
- return false
- case True:
- return true
- case String:
- return t.Str != "" && t.Str != "0" && t.Str != "false"
- case Number:
- return t.Num != 0
- }
- }
- // Int returns an integer representation.
- func (t Result) Int() int64 {
- switch t.Type {
- default:
- return 0
- case True:
- return 1
- case String:
- n, _ := parseInt(t.Str)
- return n
- case Number:
- // try to directly convert the float64 to int64
- n, ok := floatToInt(t.Num)
- if !ok {
- // now try to parse the raw string
- n, ok = parseInt(t.Raw)
- if !ok {
- // fallback to a standard conversion
- return int64(t.Num)
- }
- }
- return n
- }
- }
- // Uint returns an unsigned integer representation.
- func (t Result) Uint() uint64 {
- switch t.Type {
- default:
- return 0
- case True:
- return 1
- case String:
- n, _ := parseUint(t.Str)
- return n
- case Number:
- // try to directly convert the float64 to uint64
- n, ok := floatToUint(t.Num)
- if !ok {
- // now try to parse the raw string
- n, ok = parseUint(t.Raw)
- if !ok {
- // fallback to a standard conversion
- return uint64(t.Num)
- }
- }
- return n
- }
- }
- // Float returns an float64 representation.
- func (t Result) Float() float64 {
- switch t.Type {
- default:
- return 0
- case True:
- return 1
- case String:
- n, _ := strconv.ParseFloat(t.Str, 64)
- return n
- case Number:
- return t.Num
- }
- }
- // Time returns a time.Time representation.
- func (t Result) Time() time.Time {
- res, _ := time.Parse(time.RFC3339, t.String())
- return res
- }
- // Array returns back an array of values.
- // If the result represents a non-existent value, then an empty array will be
- // returned. If the result is not a JSON array, the return value will be an
- // array containing one result.
- func (t Result) Array() []Result {
- if t.Type == Null {
- return []Result{}
- }
- if t.Type != JSON {
- return []Result{t}
- }
- r := t.arrayOrMap('[', false)
- return r.a
- }
- // IsObject returns true if the result value is a JSON object.
- func (t Result) IsObject() bool {
- return t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '{'
- }
- // IsArray returns true if the result value is a JSON array.
- func (t Result) IsArray() bool {
- return t.Type == JSON && len(t.Raw) > 0 && t.Raw[0] == '['
- }
- // ForEach iterates through values.
- // If the result represents a non-existent value, then no values will be
- // iterated. If the result is an Object, the iterator will pass the key and
- // value of each item. If the result is an Array, the iterator will only pass
- // the value of each item. If the result is not a JSON array or object, the
- // iterator will pass back one value equal to the result.
- func (t Result) ForEach(iterator func(key, value Result) bool) {
- if !t.Exists() {
- return
- }
- if t.Type != JSON {
- iterator(Result{}, t)
- return
- }
- json := t.Raw
- var keys bool
- var i int
- var key, value Result
- for ; i < len(json); i++ {
- if json[i] == '{' {
- i++
- key.Type = String
- keys = true
- break
- } else if json[i] == '[' {
- i++
- break
- }
- if json[i] > ' ' {
- return
- }
- }
- var str string
- var vesc bool
- var ok bool
- for ; i < len(json); i++ {
- if keys {
- if json[i] != '"' {
- continue
- }
- s := i
- i, str, vesc, ok = parseString(json, i+1)
- if !ok {
- return
- }
- if vesc {
- key.Str = unescape(str[1 : len(str)-1])
- } else {
- key.Str = str[1 : len(str)-1]
- }
- key.Raw = str
- key.Index = s
- }
- for ; i < len(json); i++ {
- if json[i] <= ' ' || json[i] == ',' || json[i] == ':' {
- continue
- }
- break
- }
- s := i
- i, value, ok = parseAny(json, i, true)
- if !ok {
- return
- }
- value.Index = s
- if !iterator(key, value) {
- return
- }
- }
- }
- // Map returns back an map of values. The result should be a JSON array.
- func (t Result) Map() map[string]Result {
- if t.Type != JSON {
- return map[string]Result{}
- }
- r := t.arrayOrMap('{', false)
- return r.o
- }
- // Get searches result for the specified path.
- // The result should be a JSON array or object.
- func (t Result) Get(path string) Result {
- return Get(t.Raw, path)
- }
- type arrayOrMapResult struct {
- a []Result
- ai []interface{}
- o map[string]Result
- oi map[string]interface{}
- vc byte
- }
- func (t Result) arrayOrMap(vc byte, valueize bool) (r arrayOrMapResult) {
- var json = t.Raw
- var i int
- var value Result
- var count int
- var key Result
- if vc == 0 {
- for ; i < len(json); i++ {
- if json[i] == '{' || json[i] == '[' {
- r.vc = json[i]
- i++
- break
- }
- if json[i] > ' ' {
- goto end
- }
- }
- } else {
- for ; i < len(json); i++ {
- if json[i] == vc {
- i++
- break
- }
- if json[i] > ' ' {
- goto end
- }
- }
- r.vc = vc
- }
- if r.vc == '{' {
- if valueize {
- r.oi = make(map[string]interface{})
- } else {
- r.o = make(map[string]Result)
- }
- } else {
- if valueize {
- r.ai = make([]interface{}, 0)
- } else {
- r.a = make([]Result, 0)
- }
- }
- for ; i < len(json); i++ {
- if json[i] <= ' ' {
- continue
- }
- // get next value
- if json[i] == ']' || json[i] == '}' {
- break
- }
- switch json[i] {
- default:
- if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' {
- value.Type = Number
- value.Raw, value.Num = tonum(json[i:])
- value.Str = ""
- } else {
- continue
- }
- case '{', '[':
- value.Type = JSON
- value.Raw = squash(json[i:])
- value.Str, value.Num = "", 0
- case 'n':
- value.Type = Null
- value.Raw = tolit(json[i:])
- value.Str, value.Num = "", 0
- case 't':
- value.Type = True
- value.Raw = tolit(json[i:])
- value.Str, value.Num = "", 0
- case 'f':
- value.Type = False
- value.Raw = tolit(json[i:])
- value.Str, value.Num = "", 0
- case '"':
- value.Type = String
- value.Raw, value.Str = tostr(json[i:])
- value.Num = 0
- }
- i += len(value.Raw) - 1
- if r.vc == '{' {
- if count%2 == 0 {
- key = value
- } else {
- if valueize {
- if _, ok := r.oi[key.Str]; !ok {
- r.oi[key.Str] = value.Value()
- }
- } else {
- if _, ok := r.o[key.Str]; !ok {
- r.o[key.Str] = value
- }
- }
- }
- count++
- } else {
- if valueize {
- r.ai = append(r.ai, value.Value())
- } else {
- r.a = append(r.a, value)
- }
- }
- }
- end:
- return
- }
- // Parse parses the json and returns a result.
- //
- // This function expects that the json is well-formed, and does not validate.
- // Invalid json will not panic, but it may return back unexpected results.
- // If you are consuming JSON from an unpredictable source then you may want to
- // use the Valid function first.
- func Parse(json string) Result {
- var value Result
- for i := 0; i < len(json); i++ {
- if json[i] == '{' || json[i] == '[' {
- value.Type = JSON
- value.Raw = json[i:] // just take the entire raw
- break
- }
- if json[i] <= ' ' {
- continue
- }
- switch json[i] {
- default:
- if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' {
- value.Type = Number
- value.Raw, value.Num = tonum(json[i:])
- } else {
- return Result{}
- }
- case 'n':
- value.Type = Null
- value.Raw = tolit(json[i:])
- case 't':
- value.Type = True
- value.Raw = tolit(json[i:])
- case 'f':
- value.Type = False
- value.Raw = tolit(json[i:])
- case '"':
- value.Type = String
- value.Raw, value.Str = tostr(json[i:])
- }
- break
- }
- return value
- }
- // ParseBytes parses the json and returns a result.
- // If working with bytes, this method preferred over Parse(string(data))
- func ParseBytes(json []byte) Result {
- return Parse(string(json))
- }
- func squash(json string) string {
- // expects that the lead character is a '[' or '{'
- // squash the value, ignoring all nested arrays and objects.
- // the first '[' or '{' has already been read
- depth := 1
- for i := 1; i < len(json); i++ {
- if json[i] >= '"' && json[i] <= '}' {
- switch json[i] {
- case '"':
- i++
- s2 := i
- for ; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- // look for an escaped slash
- if json[i-1] == '\\' {
- n := 0
- for j := i - 2; j > s2-1; j-- {
- if json[j] != '\\' {
- break
- }
- n++
- }
- if n%2 == 0 {
- continue
- }
- }
- break
- }
- }
- case '{', '[':
- depth++
- case '}', ']':
- depth--
- if depth == 0 {
- return json[:i+1]
- }
- }
- }
- }
- return json
- }
- func tonum(json string) (raw string, num float64) {
- for i := 1; i < len(json); i++ {
- // less than dash might have valid characters
- if json[i] <= '-' {
- if json[i] <= ' ' || json[i] == ',' {
- // break on whitespace and comma
- raw = json[:i]
- num, _ = strconv.ParseFloat(raw, 64)
- return
- }
- // could be a '+' or '-'. let's assume so.
- continue
- }
- if json[i] < ']' {
- // probably a valid number
- continue
- }
- if json[i] == 'e' || json[i] == 'E' {
- // allow for exponential numbers
- continue
- }
- // likely a ']' or '}'
- raw = json[:i]
- num, _ = strconv.ParseFloat(raw, 64)
- return
- }
- raw = json
- num, _ = strconv.ParseFloat(raw, 64)
- return
- }
- func tolit(json string) (raw string) {
- for i := 1; i < len(json); i++ {
- if json[i] < 'a' || json[i] > 'z' {
- return json[:i]
- }
- }
- return json
- }
- func tostr(json string) (raw string, str string) {
- // expects that the lead character is a '"'
- for i := 1; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- return json[:i+1], json[1:i]
- }
- if json[i] == '\\' {
- i++
- for ; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- // look for an escaped slash
- if json[i-1] == '\\' {
- n := 0
- for j := i - 2; j > 0; j-- {
- if json[j] != '\\' {
- break
- }
- n++
- }
- if n%2 == 0 {
- continue
- }
- }
- break
- }
- }
- var ret string
- if i+1 < len(json) {
- ret = json[:i+1]
- } else {
- ret = json[:i]
- }
- return ret, unescape(json[1:i])
- }
- }
- return json, json[1:]
- }
- // Exists returns true if value exists.
- //
- // if gjson.Get(json, "name.last").Exists(){
- // println("value exists")
- // }
- func (t Result) Exists() bool {
- return t.Type != Null || len(t.Raw) != 0
- }
- // Value returns one of these types:
- //
- // bool, for JSON booleans
- // float64, for JSON numbers
- // Number, for JSON numbers
- // string, for JSON string literals
- // nil, for JSON null
- // map[string]interface{}, for JSON objects
- // []interface{}, for JSON arrays
- //
- func (t Result) Value() interface{} {
- if t.Type == String {
- return t.Str
- }
- switch t.Type {
- default:
- return nil
- case False:
- return false
- case Number:
- return t.Num
- case JSON:
- r := t.arrayOrMap(0, true)
- if r.vc == '{' {
- return r.oi
- } else if r.vc == '[' {
- return r.ai
- }
- return nil
- case True:
- return true
- }
- }
- func parseString(json string, i int) (int, string, bool, bool) {
- var s = i
- for ; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- return i + 1, json[s-1 : i+1], false, true
- }
- if json[i] == '\\' {
- i++
- for ; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- // look for an escaped slash
- if json[i-1] == '\\' {
- n := 0
- for j := i - 2; j > 0; j-- {
- if json[j] != '\\' {
- break
- }
- n++
- }
- if n%2 == 0 {
- continue
- }
- }
- return i + 1, json[s-1 : i+1], true, true
- }
- }
- break
- }
- }
- return i, json[s-1:], false, false
- }
- func parseNumber(json string, i int) (int, string) {
- var s = i
- i++
- for ; i < len(json); i++ {
- if json[i] <= ' ' || json[i] == ',' || json[i] == ']' ||
- json[i] == '}' {
- return i, json[s:i]
- }
- }
- return i, json[s:]
- }
- func parseLiteral(json string, i int) (int, string) {
- var s = i
- i++
- for ; i < len(json); i++ {
- if json[i] < 'a' || json[i] > 'z' {
- return i, json[s:i]
- }
- }
- return i, json[s:]
- }
- type arrayPathResult struct {
- part string
- path string
- pipe string
- piped bool
- more bool
- alogok bool
- arrch bool
- alogkey string
- query struct {
- on bool
- path string
- op string
- value string
- all bool
- }
- }
- func parseArrayPath(path string) (r arrayPathResult) {
- for i := 0; i < len(path); i++ {
- if path[i] == '|' {
- r.part = path[:i]
- r.pipe = path[i+1:]
- r.piped = true
- return
- }
- if path[i] == '.' {
- r.part = path[:i]
- r.path = path[i+1:]
- r.more = true
- return
- }
- if path[i] == '#' {
- r.arrch = true
- if i == 0 && len(path) > 1 {
- if path[1] == '.' {
- r.alogok = true
- r.alogkey = path[2:]
- r.path = path[:1]
- } else if path[1] == '[' || path[1] == '(' {
- // query
- r.query.on = true
- if true {
- qpath, op, value, _, fi, ok := parseQuery(path[i:])
- if !ok {
- // bad query, end now
- break
- }
- r.query.path = qpath
- r.query.op = op
- r.query.value = value
- i = fi - 1
- if i+1 < len(path) && path[i+1] == '#' {
- r.query.all = true
- }
- } else {
- var end byte
- if path[1] == '[' {
- end = ']'
- } else {
- end = ')'
- }
- i += 2
- // whitespace
- for ; i < len(path); i++ {
- if path[i] > ' ' {
- break
- }
- }
- s := i
- for ; i < len(path); i++ {
- if path[i] <= ' ' ||
- path[i] == '!' ||
- path[i] == '=' ||
- path[i] == '<' ||
- path[i] == '>' ||
- path[i] == '%' ||
- path[i] == end {
- break
- }
- }
- r.query.path = path[s:i]
- // whitespace
- for ; i < len(path); i++ {
- if path[i] > ' ' {
- break
- }
- }
- if i < len(path) {
- s = i
- if path[i] == '!' {
- if i < len(path)-1 && (path[i+1] == '=' ||
- path[i+1] == '%') {
- i++
- }
- } else if path[i] == '<' || path[i] == '>' {
- if i < len(path)-1 && path[i+1] == '=' {
- i++
- }
- } else if path[i] == '=' {
- if i < len(path)-1 && path[i+1] == '=' {
- s++
- i++
- }
- }
- i++
- r.query.op = path[s:i]
- // whitespace
- for ; i < len(path); i++ {
- if path[i] > ' ' {
- break
- }
- }
- s = i
- for ; i < len(path); i++ {
- if path[i] == '"' {
- i++
- s2 := i
- for ; i < len(path); i++ {
- if path[i] > '\\' {
- continue
- }
- if path[i] == '"' {
- // look for an escaped slash
- if path[i-1] == '\\' {
- n := 0
- for j := i - 2; j > s2-1; j-- {
- if path[j] != '\\' {
- break
- }
- n++
- }
- if n%2 == 0 {
- continue
- }
- }
- break
- }
- }
- } else if path[i] == end {
- if i+1 < len(path) && path[i+1] == '#' {
- r.query.all = true
- }
- break
- }
- }
- if i > len(path) {
- i = len(path)
- }
- v := path[s:i]
- for len(v) > 0 && v[len(v)-1] <= ' ' {
- v = v[:len(v)-1]
- }
- r.query.value = v
- }
- }
- }
- }
- continue
- }
- }
- r.part = path
- r.path = ""
- return
- }
- // splitQuery takes a query and splits it into three parts:
- // path, op, middle, and right.
- // So for this query:
- // #(first_name=="Murphy").last
- // Becomes
- // first_name # path
- // =="Murphy" # middle
- // .last # right
- // Or,
- // #(service_roles.#(=="one")).cap
- // Becomes
- // service_roles.#(=="one") # path
- // # middle
- // .cap # right
- func parseQuery(query string) (
- path, op, value, remain string, i int, ok bool,
- ) {
- if len(query) < 2 || query[0] != '#' ||
- (query[1] != '(' && query[1] != '[') {
- return "", "", "", "", i, false
- }
- i = 2
- j := 0 // start of value part
- depth := 1
- for ; i < len(query); i++ {
- if depth == 1 && j == 0 {
- switch query[i] {
- case '!', '=', '<', '>', '%':
- // start of the value part
- j = i
- continue
- }
- }
- if query[i] == '\\' {
- i++
- } else if query[i] == '[' || query[i] == '(' {
- depth++
- } else if query[i] == ']' || query[i] == ')' {
- depth--
- if depth == 0 {
- break
- }
- } else if query[i] == '"' {
- // inside selector string, balance quotes
- i++
- for ; i < len(query); i++ {
- if query[i] == '\\' {
- i++
- } else if query[i] == '"' {
- break
- }
- }
- }
- }
- if depth > 0 {
- return "", "", "", "", i, false
- }
- if j > 0 {
- path = trim(query[2:j])
- value = trim(query[j:i])
- remain = query[i+1:]
- // parse the compare op from the value
- var opsz int
- switch {
- case len(value) == 1:
- opsz = 1
- case value[0] == '!' && value[1] == '=':
- opsz = 2
- case value[0] == '!' && value[1] == '%':
- opsz = 2
- case value[0] == '<' && value[1] == '=':
- opsz = 2
- case value[0] == '>' && value[1] == '=':
- opsz = 2
- case value[0] == '=' && value[1] == '=':
- value = value[1:]
- opsz = 1
- case value[0] == '<':
- opsz = 1
- case value[0] == '>':
- opsz = 1
- case value[0] == '=':
- opsz = 1
- case value[0] == '%':
- opsz = 1
- }
- op = value[:opsz]
- value = trim(value[opsz:])
- } else {
- path = trim(query[2:i])
- remain = query[i+1:]
- }
- return path, op, value, remain, i + 1, true
- }
- func trim(s string) string {
- left:
- if len(s) > 0 && s[0] <= ' ' {
- s = s[1:]
- goto left
- }
- right:
- if len(s) > 0 && s[len(s)-1] <= ' ' {
- s = s[:len(s)-1]
- goto right
- }
- return s
- }
- type objectPathResult struct {
- part string
- path string
- pipe string
- piped bool
- wild bool
- more bool
- }
- func parseObjectPath(path string) (r objectPathResult) {
- for i := 0; i < len(path); i++ {
- if path[i] == '|' {
- r.part = path[:i]
- r.pipe = path[i+1:]
- r.piped = true
- return
- }
- if path[i] == '.' {
- // peek at the next byte and see if it's a '@', '[', or '{'.
- r.part = path[:i]
- if !DisableModifiers &&
- i < len(path)-1 &&
- (path[i+1] == '@' ||
- path[i+1] == '[' || path[i+1] == '{') {
- r.pipe = path[i+1:]
- r.piped = true
- } else {
- r.path = path[i+1:]
- r.more = true
- }
- return
- }
- if path[i] == '*' || path[i] == '?' {
- r.wild = true
- continue
- }
- if path[i] == '\\' {
- // go into escape mode. this is a slower path that
- // strips off the escape character from the part.
- epart := []byte(path[:i])
- i++
- if i < len(path) {
- epart = append(epart, path[i])
- i++
- for ; i < len(path); i++ {
- if path[i] == '\\' {
- i++
- if i < len(path) {
- epart = append(epart, path[i])
- }
- continue
- } else if path[i] == '.' {
- r.part = string(epart)
- // peek at the next byte and see if it's a '@' modifier
- if !DisableModifiers &&
- i < len(path)-1 && path[i+1] == '@' {
- r.pipe = path[i+1:]
- r.piped = true
- } else {
- r.path = path[i+1:]
- r.more = true
- }
- r.more = true
- return
- } else if path[i] == '|' {
- r.part = string(epart)
- r.pipe = path[i+1:]
- r.piped = true
- return
- } else if path[i] == '*' || path[i] == '?' {
- r.wild = true
- }
- epart = append(epart, path[i])
- }
- }
- // append the last part
- r.part = string(epart)
- return
- }
- }
- r.part = path
- return
- }
- func parseSquash(json string, i int) (int, string) {
- // expects that the lead character is a '[' or '{'
- // squash the value, ignoring all nested arrays and objects.
- // the first '[' or '{' has already been read
- s := i
- i++
- depth := 1
- for ; i < len(json); i++ {
- if json[i] >= '"' && json[i] <= '}' {
- switch json[i] {
- case '"':
- i++
- s2 := i
- for ; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- // look for an escaped slash
- if json[i-1] == '\\' {
- n := 0
- for j := i - 2; j > s2-1; j-- {
- if json[j] != '\\' {
- break
- }
- n++
- }
- if n%2 == 0 {
- continue
- }
- }
- break
- }
- }
- case '{', '[':
- depth++
- case '}', ']':
- depth--
- if depth == 0 {
- i++
- return i, json[s:i]
- }
- }
- }
- }
- return i, json[s:]
- }
- func parseObject(c *parseContext, i int, path string) (int, bool) {
- var pmatch, kesc, vesc, ok, hit bool
- var key, val string
- rp := parseObjectPath(path)
- if !rp.more && rp.piped {
- c.pipe = rp.pipe
- c.piped = true
- }
- for i < len(c.json) {
- for ; i < len(c.json); i++ {
- if c.json[i] == '"' {
- // parse_key_string
- // this is slightly different from getting s string value
- // because we don't need the outer quotes.
- i++
- var s = i
- for ; i < len(c.json); i++ {
- if c.json[i] > '\\' {
- continue
- }
- if c.json[i] == '"' {
- i, key, kesc, ok = i+1, c.json[s:i], false, true
- goto parse_key_string_done
- }
- if c.json[i] == '\\' {
- i++
- for ; i < len(c.json); i++ {
- if c.json[i] > '\\' {
- continue
- }
- if c.json[i] == '"' {
- // look for an escaped slash
- if c.json[i-1] == '\\' {
- n := 0
- for j := i - 2; j > 0; j-- {
- if c.json[j] != '\\' {
- break
- }
- n++
- }
- if n%2 == 0 {
- continue
- }
- }
- i, key, kesc, ok = i+1, c.json[s:i], true, true
- goto parse_key_string_done
- }
- }
- break
- }
- }
- key, kesc, ok = c.json[s:], false, false
- parse_key_string_done:
- break
- }
- if c.json[i] == '}' {
- return i + 1, false
- }
- }
- if !ok {
- return i, false
- }
- if rp.wild {
- if kesc {
- pmatch = match.Match(unescape(key), rp.part)
- } else {
- pmatch = match.Match(key, rp.part)
- }
- } else {
- if kesc {
- pmatch = rp.part == unescape(key)
- } else {
- pmatch = rp.part == key
- }
- }
- hit = pmatch && !rp.more
- for ; i < len(c.json); i++ {
- switch c.json[i] {
- default:
- continue
- case '"':
- i++
- i, val, vesc, ok = parseString(c.json, i)
- if !ok {
- return i, false
- }
- if hit {
- if vesc {
- c.value.Str = unescape(val[1 : len(val)-1])
- } else {
- c.value.Str = val[1 : len(val)-1]
- }
- c.value.Raw = val
- c.value.Type = String
- return i, true
- }
- case '{':
- if pmatch && !hit {
- i, hit = parseObject(c, i+1, rp.path)
- if hit {
- return i, true
- }
- } else {
- i, val = parseSquash(c.json, i)
- if hit {
- c.value.Raw = val
- c.value.Type = JSON
- return i, true
- }
- }
- case '[':
- if pmatch && !hit {
- i, hit = parseArray(c, i+1, rp.path)
- if hit {
- return i, true
- }
- } else {
- i, val = parseSquash(c.json, i)
- if hit {
- c.value.Raw = val
- c.value.Type = JSON
- return i, true
- }
- }
- case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- i, val = parseNumber(c.json, i)
- if hit {
- c.value.Raw = val
- c.value.Type = Number
- c.value.Num, _ = strconv.ParseFloat(val, 64)
- return i, true
- }
- case 't', 'f', 'n':
- vc := c.json[i]
- i, val = parseLiteral(c.json, i)
- if hit {
- c.value.Raw = val
- switch vc {
- case 't':
- c.value.Type = True
- case 'f':
- c.value.Type = False
- }
- return i, true
- }
- }
- break
- }
- }
- return i, false
- }
- func queryMatches(rp *arrayPathResult, value Result) bool {
- rpv := rp.query.value
- if len(rpv) > 2 && rpv[0] == '"' && rpv[len(rpv)-1] == '"' {
- rpv = rpv[1 : len(rpv)-1]
- }
- if !value.Exists() {
- return false
- }
- if rp.query.op == "" {
- // the query is only looking for existence, such as:
- // friends.#(name)
- // which makes sure that the array "friends" has an element of
- // "name" that exists
- return true
- }
- switch value.Type {
- case String:
- switch rp.query.op {
- case "=":
- return value.Str == rpv
- case "!=":
- return value.Str != rpv
- case "<":
- return value.Str < rpv
- case "<=":
- return value.Str <= rpv
- case ">":
- return value.Str > rpv
- case ">=":
- return value.Str >= rpv
- case "%":
- return match.Match(value.Str, rpv)
- case "!%":
- return !match.Match(value.Str, rpv)
- }
- case Number:
- rpvn, _ := strconv.ParseFloat(rpv, 64)
- switch rp.query.op {
- case "=":
- return value.Num == rpvn
- case "!=":
- return value.Num != rpvn
- case "<":
- return value.Num < rpvn
- case "<=":
- return value.Num <= rpvn
- case ">":
- return value.Num > rpvn
- case ">=":
- return value.Num >= rpvn
- }
- case True:
- switch rp.query.op {
- case "=":
- return rpv == "true"
- case "!=":
- return rpv != "true"
- case ">":
- return rpv == "false"
- case ">=":
- return true
- }
- case False:
- switch rp.query.op {
- case "=":
- return rpv == "false"
- case "!=":
- return rpv != "false"
- case "<":
- return rpv == "true"
- case "<=":
- return true
- }
- }
- return false
- }
- func parseArray(c *parseContext, i int, path string) (int, bool) {
- var pmatch, vesc, ok, hit bool
- var val string
- var h int
- var alog []int
- var partidx int
- var multires []byte
- rp := parseArrayPath(path)
- if !rp.arrch {
- n, ok := parseUint(rp.part)
- if !ok {
- partidx = -1
- } else {
- partidx = int(n)
- }
- }
- if !rp.more && rp.piped {
- c.pipe = rp.pipe
- c.piped = true
- }
- procQuery := func(qval Result) bool {
- if rp.query.all {
- if len(multires) == 0 {
- multires = append(multires, '[')
- }
- }
- var res Result
- if qval.Type == JSON {
- res = qval.Get(rp.query.path)
- } else {
- if rp.query.path != "" {
- return false
- }
- res = qval
- }
- if queryMatches(&rp, res) {
- if rp.more {
- left, right, ok := splitPossiblePipe(rp.path)
- if ok {
- rp.path = left
- c.pipe = right
- c.piped = true
- }
- res = qval.Get(rp.path)
- } else {
- res = qval
- }
- if rp.query.all {
- raw := res.Raw
- if len(raw) == 0 {
- raw = res.String()
- }
- if raw != "" {
- if len(multires) > 1 {
- multires = append(multires, ',')
- }
- multires = append(multires, raw...)
- }
- } else {
- c.value = res
- return true
- }
- }
- return false
- }
- for i < len(c.json)+1 {
- if !rp.arrch {
- pmatch = partidx == h
- hit = pmatch && !rp.more
- }
- h++
- if rp.alogok {
- alog = append(alog, i)
- }
- for ; ; i++ {
- var ch byte
- if i > len(c.json) {
- break
- } else if i == len(c.json) {
- ch = ']'
- } else {
- ch = c.json[i]
- }
- switch ch {
- default:
- continue
- case '"':
- i++
- i, val, vesc, ok = parseString(c.json, i)
- if !ok {
- return i, false
- }
- if rp.query.on {
- var qval Result
- if vesc {
- qval.Str = unescape(val[1 : len(val)-1])
- } else {
- qval.Str = val[1 : len(val)-1]
- }
- qval.Raw = val
- qval.Type = String
- if procQuery(qval) {
- return i, true
- }
- } else if hit {
- if rp.alogok {
- break
- }
- if vesc {
- c.value.Str = unescape(val[1 : len(val)-1])
- } else {
- c.value.Str = val[1 : len(val)-1]
- }
- c.value.Raw = val
- c.value.Type = String
- return i, true
- }
- case '{':
- if pmatch && !hit {
- i, hit = parseObject(c, i+1, rp.path)
- if hit {
- if rp.alogok {
- break
- }
- return i, true
- }
- } else {
- i, val = parseSquash(c.json, i)
- if rp.query.on {
- if procQuery(Result{Raw: val, Type: JSON}) {
- return i, true
- }
- } else if hit {
- if rp.alogok {
- break
- }
- c.value.Raw = val
- c.value.Type = JSON
- return i, true
- }
- }
- case '[':
- if pmatch && !hit {
- i, hit = parseArray(c, i+1, rp.path)
- if hit {
- if rp.alogok {
- break
- }
- return i, true
- }
- } else {
- i, val = parseSquash(c.json, i)
- if rp.query.on {
- if procQuery(Result{Raw: val, Type: JSON}) {
- return i, true
- }
- } else if hit {
- if rp.alogok {
- break
- }
- c.value.Raw = val
- c.value.Type = JSON
- return i, true
- }
- }
- case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- i, val = parseNumber(c.json, i)
- if rp.query.on {
- var qval Result
- qval.Raw = val
- qval.Type = Number
- qval.Num, _ = strconv.ParseFloat(val, 64)
- if procQuery(qval) {
- return i, true
- }
- } else if hit {
- if rp.alogok {
- break
- }
- c.value.Raw = val
- c.value.Type = Number
- c.value.Num, _ = strconv.ParseFloat(val, 64)
- return i, true
- }
- case 't', 'f', 'n':
- vc := c.json[i]
- i, val = parseLiteral(c.json, i)
- if rp.query.on {
- var qval Result
- qval.Raw = val
- switch vc {
- case 't':
- qval.Type = True
- case 'f':
- qval.Type = False
- }
- if procQuery(qval) {
- return i, true
- }
- } else if hit {
- if rp.alogok {
- break
- }
- c.value.Raw = val
- switch vc {
- case 't':
- c.value.Type = True
- case 'f':
- c.value.Type = False
- }
- return i, true
- }
- case ']':
- if rp.arrch && rp.part == "#" {
- if rp.alogok {
- left, right, ok := splitPossiblePipe(rp.alogkey)
- if ok {
- rp.alogkey = left
- c.pipe = right
- c.piped = true
- }
- var jsons = make([]byte, 0, 64)
- jsons = append(jsons, '[')
- for j, k := 0, 0; j < len(alog); j++ {
- _, res, ok := parseAny(c.json, alog[j], true)
- if ok {
- res := res.Get(rp.alogkey)
- if res.Exists() {
- if k > 0 {
- jsons = append(jsons, ',')
- }
- raw := res.Raw
- if len(raw) == 0 {
- raw = res.String()
- }
- jsons = append(jsons, []byte(raw)...)
- k++
- }
- }
- }
- jsons = append(jsons, ']')
- c.value.Type = JSON
- c.value.Raw = string(jsons)
- return i + 1, true
- }
- if rp.alogok {
- break
- }
- c.value.Type = Number
- c.value.Num = float64(h - 1)
- c.value.Raw = strconv.Itoa(h - 1)
- c.calcd = true
- return i + 1, true
- }
- if len(multires) > 0 && !c.value.Exists() {
- c.value = Result{
- Raw: string(append(multires, ']')),
- Type: JSON,
- }
- }
- return i + 1, false
- }
- break
- }
- }
- return i, false
- }
- func splitPossiblePipe(path string) (left, right string, ok bool) {
- // take a quick peek for the pipe character. If found we'll split the piped
- // part of the path into the c.pipe field and shorten the rp.
- var possible bool
- for i := 0; i < len(path); i++ {
- if path[i] == '|' {
- possible = true
- break
- }
- }
- if !possible {
- return
- }
- // split the left and right side of the path with the pipe character as
- // the delimiter. This is a little tricky because we'll need to basically
- // parse the entire path.
- for i := 0; i < len(path); i++ {
- if path[i] == '\\' {
- i++
- } else if path[i] == '.' {
- if i == len(path)-1 {
- return
- }
- if path[i+1] == '#' {
- i += 2
- if i == len(path) {
- return
- }
- if path[i] == '[' || path[i] == '(' {
- var start, end byte
- if path[i] == '[' {
- start, end = '[', ']'
- } else {
- start, end = '(', ')'
- }
- // inside selector, balance brackets
- i++
- depth := 1
- for ; i < len(path); i++ {
- if path[i] == '\\' {
- i++
- } else if path[i] == start {
- depth++
- } else if path[i] == end {
- depth--
- if depth == 0 {
- break
- }
- } else if path[i] == '"' {
- // inside selector string, balance quotes
- i++
- for ; i < len(path); i++ {
- if path[i] == '\\' {
- i++
- } else if path[i] == '"' {
- break
- }
- }
- }
- }
- }
- }
- } else if path[i] == '|' {
- return path[:i], path[i+1:], true
- }
- }
- return
- }
- // ForEachLine iterates through lines of JSON as specified by the JSON Lines
- // format (http://jsonlines.org/).
- // Each line is returned as a GJSON Result.
- func ForEachLine(json string, iterator func(line Result) bool) {
- var res Result
- var i int
- for {
- i, res, _ = parseAny(json, i, true)
- if !res.Exists() {
- break
- }
- if !iterator(res) {
- return
- }
- }
- }
- type subSelector struct {
- name string
- path string
- }
- // parseSubSelectors returns the subselectors belonging to a '[path1,path2]' or
- // '{"field1":path1,"field2":path2}' type subSelection. It's expected that the
- // first character in path is either '[' or '{', and has already been checked
- // prior to calling this function.
- func parseSubSelectors(path string) (sels []subSelector, out string, ok bool) {
- depth := 1
- colon := 0
- start := 1
- i := 1
- pushSel := func() {
- var sel subSelector
- if colon == 0 {
- sel.path = path[start:i]
- } else {
- sel.name = path[start:colon]
- sel.path = path[colon+1 : i]
- }
- sels = append(sels, sel)
- colon = 0
- start = i + 1
- }
- for ; i < len(path); i++ {
- switch path[i] {
- case '\\':
- i++
- case ':':
- if depth == 1 {
- colon = i
- }
- case ',':
- if depth == 1 {
- pushSel()
- }
- case '"':
- i++
- loop:
- for ; i < len(path); i++ {
- switch path[i] {
- case '\\':
- i++
- case '"':
- break loop
- }
- }
- case '[', '(', '{':
- depth++
- case ']', ')', '}':
- depth--
- if depth == 0 {
- pushSel()
- path = path[i+1:]
- return sels, path, true
- }
- }
- }
- return
- }
- // nameOfLast returns the name of the last component
- func nameOfLast(path string) string {
- for i := len(path) - 1; i >= 0; i-- {
- if path[i] == '|' || path[i] == '.' {
- if i > 0 {
- if path[i-1] == '\\' {
- continue
- }
- }
- return path[i+1:]
- }
- }
- return path
- }
- func isSimpleName(component string) bool {
- for i := 0; i < len(component); i++ {
- if component[i] < ' ' {
- return false
- }
- switch component[i] {
- case '[', ']', '{', '}', '(', ')', '#', '|':
- return false
- }
- }
- return true
- }
- func appendJSONString(dst []byte, s string) []byte {
- for i := 0; i < len(s); i++ {
- if s[i] < ' ' || s[i] == '\\' || s[i] == '"' || s[i] > 126 {
- d, _ := json.Marshal(s)
- return append(dst, string(d)...)
- }
- }
- dst = append(dst, '"')
- dst = append(dst, s...)
- dst = append(dst, '"')
- return dst
- }
- type parseContext struct {
- json string
- value Result
- pipe string
- piped bool
- calcd bool
- lines bool
- }
- // Get searches json for the specified path.
- // A path is in dot syntax, such as "name.last" or "age".
- // When the value is found it's returned immediately.
- //
- // A path is a series of keys searated by a dot.
- // A key may contain special wildcard characters '*' and '?'.
- // To access an array value use the index as the key.
- // To get the number of elements in an array or to access a child path, use
- // the '#' character.
- // The dot and wildcard character can be escaped with '\'.
- //
- // {
- // "name": {"first": "Tom", "last": "Anderson"},
- // "age":37,
- // "children": ["Sara","Alex","Jack"],
- // "friends": [
- // {"first": "James", "last": "Murphy"},
- // {"first": "Roger", "last": "Craig"}
- // ]
- // }
- // "name.last" >> "Anderson"
- // "age" >> 37
- // "children" >> ["Sara","Alex","Jack"]
- // "children.#" >> 3
- // "children.1" >> "Alex"
- // "child*.2" >> "Jack"
- // "c?ildren.0" >> "Sara"
- // "friends.#.first" >> ["James","Roger"]
- //
- // This function expects that the json is well-formed, and does not validate.
- // Invalid json will not panic, but it may return back unexpected results.
- // If you are consuming JSON from an unpredictable source then you may want to
- // 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
- 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
- return res
- }
- return Parse(rjson)
- }
- }
- }
- if path[0] == '[' || path[0] == '{' {
- // using a subselector path
- kind := path[0]
- var ok bool
- var subs []subSelector
- subs, path, ok = parseSubSelectors(path)
- if ok {
- if len(path) == 0 || (path[0] == '|' || path[0] == '.') {
- var b []byte
- b = append(b, kind)
- var i int
- for _, sub := range subs {
- res := Get(json, sub.path)
- if res.Exists() {
- if i > 0 {
- b = append(b, ',')
- }
- if kind == '{' {
- if len(sub.name) > 0 {
- if sub.name[0] == '"' && Valid(sub.name) {
- b = append(b, sub.name...)
- } else {
- b = appendJSONString(b, sub.name)
- }
- } else {
- last := nameOfLast(sub.path)
- if isSimpleName(last) {
- b = appendJSONString(b, last)
- } else {
- b = appendJSONString(b, "_")
- }
- }
- b = append(b, ':')
- }
- var raw string
- if len(res.Raw) == 0 {
- raw = res.String()
- if len(raw) == 0 {
- raw = "null"
- }
- } else {
- raw = res.Raw
- }
- b = append(b, raw...)
- i++
- }
- }
- b = append(b, kind+2)
- var res Result
- res.Raw = string(b)
- res.Type = JSON
- if len(path) > 0 {
- res = res.Get(path[1:])
- }
- res.Index = 0
- return res
- }
- }
- }
- }
- var i int
- var c = &parseContext{json: json}
- if len(path) >= 2 && path[0] == '.' && path[1] == '.' {
- c.lines = true
- parseArray(c, 0, path[2:])
- } else {
- for ; i < len(c.json); i++ {
- if c.json[i] == '{' {
- i++
- parseObject(c, i, path)
- break
- }
- if c.json[i] == '[' {
- i++
- parseArray(c, i, path)
- break
- }
- }
- }
- if c.piped {
- res := c.value.Get(c.pipe)
- res.Index = 0
- return res
- }
- fillIndex(json, c)
- return c.value
- }
- // GetBytes searches json for the specified path.
- // If working with bytes, this method preferred over Get(string(data), path)
- func GetBytes(json []byte, path string) Result {
- return getBytes(json, path)
- }
- // runeit returns the rune from the the \uXXXX
- func runeit(json string) rune {
- n, _ := strconv.ParseUint(json[:4], 16, 64)
- return rune(n)
- }
- // unescape unescapes a string
- func unescape(json string) string { //, error) {
- var str = make([]byte, 0, len(json))
- for i := 0; i < len(json); i++ {
- switch {
- default:
- str = append(str, json[i])
- case json[i] < ' ':
- return string(str)
- case json[i] == '\\':
- i++
- if i >= len(json) {
- return string(str)
- }
- switch json[i] {
- default:
- return string(str)
- case '\\':
- str = append(str, '\\')
- case '/':
- str = append(str, '/')
- case 'b':
- str = append(str, '\b')
- case 'f':
- str = append(str, '\f')
- case 'n':
- str = append(str, '\n')
- case 'r':
- str = append(str, '\r')
- case 't':
- str = append(str, '\t')
- case '"':
- str = append(str, '"')
- case 'u':
- if i+5 > len(json) {
- return string(str)
- }
- r := runeit(json[i+1:])
- i += 5
- if utf16.IsSurrogate(r) {
- // need another code
- if len(json[i:]) >= 6 && json[i] == '\\' &&
- json[i+1] == 'u' {
- // we expect it to be correct so just consume it
- r = utf16.DecodeRune(r, runeit(json[i+2:]))
- i += 6
- }
- }
- // provide enough space to encode the largest utf8 possible
- str = append(str, 0, 0, 0, 0, 0, 0, 0, 0)
- n := utf8.EncodeRune(str[len(str)-8:], r)
- str = str[:len(str)-8+n]
- i-- // backtrack index by one
- }
- }
- }
- return string(str)
- }
- // Less return true if a token is less than another token.
- // The caseSensitive paramater is used when the tokens are Strings.
- // The order when comparing two different type is:
- //
- // Null < False < Number < String < True < JSON
- //
- func (t Result) Less(token Result, caseSensitive bool) bool {
- if t.Type < token.Type {
- return true
- }
- if t.Type > token.Type {
- return false
- }
- if t.Type == String {
- if caseSensitive {
- return t.Str < token.Str
- }
- return stringLessInsensitive(t.Str, token.Str)
- }
- if t.Type == Number {
- return t.Num < token.Num
- }
- return t.Raw < token.Raw
- }
- func stringLessInsensitive(a, b string) bool {
- for i := 0; i < len(a) && i < len(b); i++ {
- if a[i] >= 'A' && a[i] <= 'Z' {
- if b[i] >= 'A' && b[i] <= 'Z' {
- // both are uppercase, do nothing
- if a[i] < b[i] {
- return true
- } else if a[i] > b[i] {
- return false
- }
- } else {
- // a is uppercase, convert a to lowercase
- if a[i]+32 < b[i] {
- return true
- } else if a[i]+32 > b[i] {
- return false
- }
- }
- } else if b[i] >= 'A' && b[i] <= 'Z' {
- // b is uppercase, convert b to lowercase
- if a[i] < b[i]+32 {
- return true
- } else if a[i] > b[i]+32 {
- return false
- }
- } else {
- // neither are uppercase
- if a[i] < b[i] {
- return true
- } else if a[i] > b[i] {
- return false
- }
- }
- }
- return len(a) < len(b)
- }
- // parseAny parses the next value from a json string.
- // A Result is returned when the hit param is set.
- // The return values are (i int, res Result, ok bool)
- func parseAny(json string, i int, hit bool) (int, Result, bool) {
- var res Result
- var val string
- for ; i < len(json); i++ {
- if json[i] == '{' || json[i] == '[' {
- i, val = parseSquash(json, i)
- if hit {
- res.Raw = val
- res.Type = JSON
- }
- return i, res, true
- }
- if json[i] <= ' ' {
- continue
- }
- switch json[i] {
- case '"':
- i++
- var vesc bool
- var ok bool
- i, val, vesc, ok = parseString(json, i)
- if !ok {
- return i, res, false
- }
- if hit {
- res.Type = String
- res.Raw = val
- if vesc {
- res.Str = unescape(val[1 : len(val)-1])
- } else {
- res.Str = val[1 : len(val)-1]
- }
- }
- return i, res, true
- case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- i, val = parseNumber(json, i)
- if hit {
- res.Raw = val
- res.Type = Number
- res.Num, _ = strconv.ParseFloat(val, 64)
- }
- return i, res, true
- case 't', 'f', 'n':
- vc := json[i]
- i, val = parseLiteral(json, i)
- if hit {
- res.Raw = val
- switch vc {
- case 't':
- res.Type = True
- case 'f':
- res.Type = False
- }
- return i, res, true
- }
- }
- }
- return i, res, false
- }
- var ( // used for testing
- testWatchForFallback bool
- testLastWasFallback bool
- )
- // GetMany searches json for the multiple paths.
- // The return value is a Result array where the number of items
- // will be equal to the number of input paths.
- func GetMany(json string, path ...string) []Result {
- res := make([]Result, len(path))
- for i, path := range path {
- res[i] = Get(json, path)
- }
- return res
- }
- // GetManyBytes searches json for the multiple paths.
- // The return value is a Result array where the number of items
- // will be equal to the number of input paths.
- func GetManyBytes(json []byte, path ...string) []Result {
- res := make([]Result, len(path))
- for i, path := range path {
- res[i] = GetBytes(json, path)
- }
- return res
- }
- var fieldsmu sync.RWMutex
- var fields = make(map[string]map[string]int)
- func assign(jsval Result, goval reflect.Value) {
- if jsval.Type == Null {
- return
- }
- switch goval.Kind() {
- default:
- case reflect.Ptr:
- if !goval.IsNil() {
- newval := reflect.New(goval.Elem().Type())
- assign(jsval, newval.Elem())
- goval.Elem().Set(newval.Elem())
- } else {
- newval := reflect.New(goval.Type().Elem())
- assign(jsval, newval.Elem())
- goval.Set(newval)
- }
- case reflect.Struct:
- fieldsmu.RLock()
- sf := fields[goval.Type().String()]
- fieldsmu.RUnlock()
- if sf == nil {
- fieldsmu.Lock()
- sf = make(map[string]int)
- for i := 0; i < goval.Type().NumField(); i++ {
- f := goval.Type().Field(i)
- tag := strings.Split(f.Tag.Get("json"), ",")[0]
- if tag != "-" {
- if tag != "" {
- sf[tag] = i
- sf[f.Name] = i
- } else {
- sf[f.Name] = i
- }
- }
- }
- fields[goval.Type().String()] = sf
- fieldsmu.Unlock()
- }
- jsval.ForEach(func(key, value Result) bool {
- if idx, ok := sf[key.Str]; ok {
- f := goval.Field(idx)
- if f.CanSet() {
- assign(value, f)
- }
- }
- return true
- })
- case reflect.Slice:
- if goval.Type().Elem().Kind() == reflect.Uint8 &&
- jsval.Type == String {
- data, _ := base64.StdEncoding.DecodeString(jsval.String())
- goval.Set(reflect.ValueOf(data))
- } else {
- jsvals := jsval.Array()
- slice := reflect.MakeSlice(goval.Type(), len(jsvals), len(jsvals))
- for i := 0; i < len(jsvals); i++ {
- assign(jsvals[i], slice.Index(i))
- }
- goval.Set(slice)
- }
- case reflect.Array:
- i, n := 0, goval.Len()
- jsval.ForEach(func(_, value Result) bool {
- if i == n {
- return false
- }
- assign(value, goval.Index(i))
- i++
- return true
- })
- case reflect.Map:
- if goval.Type().Key().Kind() == reflect.String &&
- goval.Type().Elem().Kind() == reflect.Interface {
- goval.Set(reflect.ValueOf(jsval.Value()))
- }
- case reflect.Interface:
- goval.Set(reflect.ValueOf(jsval.Value()))
- case reflect.Bool:
- goval.SetBool(jsval.Bool())
- case reflect.Float32, reflect.Float64:
- goval.SetFloat(jsval.Float())
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
- reflect.Int64:
- goval.SetInt(jsval.Int())
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
- reflect.Uint64:
- goval.SetUint(jsval.Uint())
- case reflect.String:
- goval.SetString(jsval.String())
- }
- if len(goval.Type().PkgPath()) > 0 {
- v := goval.Addr()
- if v.Type().NumMethod() > 0 {
- if u, ok := v.Interface().(json.Unmarshaler); ok {
- u.UnmarshalJSON([]byte(jsval.Raw))
- }
- }
- }
- }
- var validate uintptr = 1
- // UnmarshalValidationEnabled provides the option to disable JSON validation
- // during the Unmarshal routine. Validation is enabled by default.
- //
- // Deprecated: Use encoder/json.Unmarshal instead
- func UnmarshalValidationEnabled(enabled bool) {
- if enabled {
- atomic.StoreUintptr(&validate, 1)
- } else {
- atomic.StoreUintptr(&validate, 0)
- }
- }
- // Unmarshal loads the JSON data into the value pointed to by v.
- //
- // This function works almost identically to json.Unmarshal except that
- // gjson.Unmarshal will automatically attempt to convert JSON values to any Go
- // type. For example, the JSON string "100" or the JSON number 100 can be
- // equally assigned to Go string, int, byte, uint64, etc. This rule applies to
- // all types.
- //
- // Deprecated: Use encoder/json.Unmarshal instead
- func Unmarshal(data []byte, v interface{}) error {
- if atomic.LoadUintptr(&validate) == 1 {
- _, ok := validpayload(data, 0)
- if !ok {
- return errors.New("invalid json")
- }
- }
- if v := reflect.ValueOf(v); v.Kind() == reflect.Ptr {
- assign(ParseBytes(data), v)
- }
- return nil
- }
- func validpayload(data []byte, i int) (outi int, ok bool) {
- for ; i < len(data); i++ {
- switch data[i] {
- default:
- i, ok = validany(data, i)
- if !ok {
- return i, false
- }
- for ; i < len(data); i++ {
- switch data[i] {
- default:
- return i, false
- case ' ', '\t', '\n', '\r':
- continue
- }
- }
- return i, true
- case ' ', '\t', '\n', '\r':
- continue
- }
- }
- return i, false
- }
- func validany(data []byte, i int) (outi int, ok bool) {
- for ; i < len(data); i++ {
- switch data[i] {
- default:
- return i, false
- case ' ', '\t', '\n', '\r':
- continue
- case '{':
- return validobject(data, i+1)
- case '[':
- return validarray(data, i+1)
- case '"':
- return validstring(data, i+1)
- case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- return validnumber(data, i+1)
- case 't':
- return validtrue(data, i+1)
- case 'f':
- return validfalse(data, i+1)
- case 'n':
- return validnull(data, i+1)
- }
- }
- return i, false
- }
- func validobject(data []byte, i int) (outi int, ok bool) {
- for ; i < len(data); i++ {
- switch data[i] {
- default:
- return i, false
- case ' ', '\t', '\n', '\r':
- continue
- case '}':
- return i + 1, true
- case '"':
- key:
- if i, ok = validstring(data, i+1); !ok {
- return i, false
- }
- if i, ok = validcolon(data, i); !ok {
- return i, false
- }
- if i, ok = validany(data, i); !ok {
- return i, false
- }
- if i, ok = validcomma(data, i, '}'); !ok {
- return i, false
- }
- if data[i] == '}' {
- return i + 1, true
- }
- i++
- for ; i < len(data); i++ {
- switch data[i] {
- default:
- return i, false
- case ' ', '\t', '\n', '\r':
- continue
- case '"':
- goto key
- }
- }
- return i, false
- }
- }
- return i, false
- }
- func validcolon(data []byte, i int) (outi int, ok bool) {
- for ; i < len(data); i++ {
- switch data[i] {
- default:
- return i, false
- case ' ', '\t', '\n', '\r':
- continue
- case ':':
- return i + 1, true
- }
- }
- return i, false
- }
- func validcomma(data []byte, i int, end byte) (outi int, ok bool) {
- for ; i < len(data); i++ {
- switch data[i] {
- default:
- return i, false
- case ' ', '\t', '\n', '\r':
- continue
- case ',':
- return i, true
- case end:
- return i, true
- }
- }
- return i, false
- }
- func validarray(data []byte, i int) (outi int, ok bool) {
- for ; i < len(data); i++ {
- switch data[i] {
- default:
- for ; i < len(data); i++ {
- if i, ok = validany(data, i); !ok {
- return i, false
- }
- if i, ok = validcomma(data, i, ']'); !ok {
- return i, false
- }
- if data[i] == ']' {
- return i + 1, true
- }
- }
- case ' ', '\t', '\n', '\r':
- continue
- case ']':
- return i + 1, true
- }
- }
- return i, false
- }
- func validstring(data []byte, i int) (outi int, ok bool) {
- for ; i < len(data); i++ {
- if data[i] < ' ' {
- return i, false
- } else if data[i] == '\\' {
- i++
- if i == len(data) {
- return i, false
- }
- switch data[i] {
- default:
- return i, false
- case '"', '\\', '/', 'b', 'f', 'n', 'r', 't':
- case 'u':
- for j := 0; j < 4; j++ {
- i++
- if i >= len(data) {
- return i, false
- }
- if !((data[i] >= '0' && data[i] <= '9') ||
- (data[i] >= 'a' && data[i] <= 'f') ||
- (data[i] >= 'A' && data[i] <= 'F')) {
- return i, false
- }
- }
- }
- } else if data[i] == '"' {
- return i + 1, true
- }
- }
- return i, false
- }
- func validnumber(data []byte, i int) (outi int, ok bool) {
- i--
- // sign
- if data[i] == '-' {
- i++
- }
- // int
- if i == len(data) {
- return i, false
- }
- if data[i] == '0' {
- i++
- } else {
- for ; i < len(data); i++ {
- if data[i] >= '0' && data[i] <= '9' {
- continue
- }
- break
- }
- }
- // frac
- if i == len(data) {
- return i, true
- }
- if data[i] == '.' {
- i++
- if i == len(data) {
- return i, false
- }
- if data[i] < '0' || data[i] > '9' {
- return i, false
- }
- i++
- for ; i < len(data); i++ {
- if data[i] >= '0' && data[i] <= '9' {
- continue
- }
- break
- }
- }
- // exp
- if i == len(data) {
- return i, true
- }
- if data[i] == 'e' || data[i] == 'E' {
- i++
- if i == len(data) {
- return i, false
- }
- if data[i] == '+' || data[i] == '-' {
- i++
- }
- if i == len(data) {
- return i, false
- }
- if data[i] < '0' || data[i] > '9' {
- return i, false
- }
- i++
- for ; i < len(data); i++ {
- if data[i] >= '0' && data[i] <= '9' {
- continue
- }
- break
- }
- }
- return i, true
- }
- func validtrue(data []byte, i int) (outi int, ok bool) {
- if i+3 <= len(data) && data[i] == 'r' && data[i+1] == 'u' &&
- data[i+2] == 'e' {
- return i + 3, true
- }
- return i, false
- }
- func validfalse(data []byte, i int) (outi int, ok bool) {
- if i+4 <= len(data) && data[i] == 'a' && data[i+1] == 'l' &&
- data[i+2] == 's' && data[i+3] == 'e' {
- return i + 4, true
- }
- return i, false
- }
- func validnull(data []byte, i int) (outi int, ok bool) {
- if i+3 <= len(data) && data[i] == 'u' && data[i+1] == 'l' &&
- data[i+2] == 'l' {
- return i + 3, true
- }
- return i, false
- }
- // Valid returns true if the input is valid json.
- //
- // if !gjson.Valid(json) {
- // return errors.New("invalid json")
- // }
- // value := gjson.Get(json, "name.last")
- //
- func Valid(json string) bool {
- _, ok := validpayload(stringBytes(json), 0)
- return ok
- }
- // ValidBytes returns true if the input is valid json.
- //
- // if !gjson.Valid(json) {
- // return errors.New("invalid json")
- // }
- // value := gjson.Get(json, "name.last")
- //
- // If working with bytes, this method preferred over ValidBytes(string(data))
- //
- func ValidBytes(json []byte) bool {
- _, ok := validpayload(json, 0)
- return ok
- }
- func parseUint(s string) (n uint64, ok bool) {
- var i int
- if i == len(s) {
- return 0, false
- }
- for ; i < len(s); i++ {
- if s[i] >= '0' && s[i] <= '9' {
- n = n*10 + uint64(s[i]-'0')
- } else {
- return 0, false
- }
- }
- return n, true
- }
- func parseInt(s string) (n int64, ok bool) {
- var i int
- var sign bool
- if len(s) > 0 && s[0] == '-' {
- sign = true
- i++
- }
- if i == len(s) {
- return 0, false
- }
- for ; i < len(s); i++ {
- if s[i] >= '0' && s[i] <= '9' {
- n = n*10 + int64(s[i]-'0')
- } else {
- return 0, false
- }
- }
- if sign {
- return n * -1, true
- }
- return n, true
- }
- const minUint53 = 0
- const maxUint53 = 4503599627370495
- const minInt53 = -2251799813685248
- const maxInt53 = 2251799813685247
- func floatToUint(f float64) (n uint64, ok bool) {
- n = uint64(f)
- if float64(n) == f && n >= minUint53 && n <= maxUint53 {
- return n, true
- }
- return 0, false
- }
- func floatToInt(f float64) (n int64, ok bool) {
- n = int64(f)
- if float64(n) == f && n >= minInt53 && n <= maxInt53 {
- return n, true
- }
- return 0, false
- }
- // execModifier parses the path to find a matching modifier function.
- // then 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
- for i := 1; i < len(path); i++ {
- if path[i] == ':' {
- pathOut = path[i+1:]
- name = path[1:i]
- hasArgs = len(pathOut) > 0
- break
- }
- if path[i] == '|' {
- pathOut = path[i:]
- name = path[1:i]
- break
- }
- if path[i] == '.' {
- pathOut = path[i:]
- name = path[1:i]
- break
- }
- }
- if fn, ok := modifiers[name]; ok {
- var args string
- if hasArgs {
- var parsedArgs bool
- switch pathOut[0] {
- case '{', '[', '"':
- res := Parse(pathOut)
- if res.Exists() {
- _, args = parseSquash(pathOut, 0)
- pathOut = pathOut[len(args):]
- parsedArgs = true
- }
- }
- if !parsedArgs {
- idx := strings.IndexByte(pathOut, '|')
- if idx == -1 {
- args = pathOut
- pathOut = ""
- } else {
- args = pathOut[:idx]
- pathOut = pathOut[idx:]
- }
- }
- }
- return pathOut, fn(json, args), true
- }
- return pathOut, res, false
- }
- // DisableModifiers will disable the modifier syntax
- var DisableModifiers = false
- var modifiers = map[string]func(json, arg string) string{
- "pretty": modPretty,
- "ugly": modUgly,
- "reverse": modReverse,
- }
- // AddModifier binds a custom modifier command to the GJSON syntax.
- // This operation is not thread safe and should be executed prior to
- // using all other gjson function.
- func AddModifier(name string, fn func(json, arg string) string) {
- modifiers[name] = fn
- }
- // ModifierExists returns true when the specified modifier exists.
- func ModifierExists(name string, fn func(json, arg string) string) bool {
- _, ok := modifiers[name]
- return ok
- }
- // @pretty modifier makes the json look nice.
- func modPretty(json, arg string) string {
- if len(arg) > 0 {
- opts := *pretty.DefaultOptions
- Parse(arg).ForEach(func(key, value Result) bool {
- switch key.String() {
- case "sortKeys":
- opts.SortKeys = value.Bool()
- case "indent":
- opts.Indent = value.String()
- case "prefix":
- opts.Prefix = value.String()
- case "width":
- opts.Width = int(value.Int())
- }
- return true
- })
- return bytesString(pretty.PrettyOptions(stringBytes(json), &opts))
- }
- return bytesString(pretty.Pretty(stringBytes(json)))
- }
- // @ugly modifier removes all whitespace.
- func modUgly(json, arg string) string {
- return bytesString(pretty.Ugly(stringBytes(json)))
- }
- // @reverse reverses array elements or root object members.
- func modReverse(json, arg string) string {
- res := Parse(json)
- if res.IsArray() {
- var values []Result
- res.ForEach(func(_, value Result) bool {
- values = append(values, value)
- return true
- })
- out := make([]byte, 0, len(json))
- out = append(out, '[')
- for i, j := len(values)-1, 0; i >= 0; i, j = i-1, j+1 {
- if j > 0 {
- out = append(out, ',')
- }
- out = append(out, values[i].Raw...)
- }
- out = append(out, ']')
- return bytesString(out)
- }
- if res.IsObject() {
- var keyValues []Result
- res.ForEach(func(key, value Result) bool {
- keyValues = append(keyValues, key, value)
- return true
- })
- out := make([]byte, 0, len(json))
- out = append(out, '{')
- for i, j := len(keyValues)-2, 0; i >= 0; i, j = i-2, j+1 {
- if j > 0 {
- out = append(out, ',')
- }
- out = append(out, keyValues[i+0].Raw...)
- out = append(out, ':')
- out = append(out, keyValues[i+1].Raw...)
- }
- out = append(out, '}')
- return bytesString(out)
- }
- return json
- }
|