1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214 |
- // Package gjson provides searching for json strings.
- package gjson
-
- import (
- "encoding/json"
- "strconv"
- "strings"
- "time"
- "unicode/utf16"
- "unicode/utf8"
- "unsafe"
-
- "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
- // Indexes of all the elements that match on a path containing the '#'
- // query character.
- Indexes []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:
- b, _ := strconv.ParseBool(strings.ToLower(t.Str))
- return b
- 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
- i, ok := safeInt(t.Num)
- if ok {
- return i
- }
- // now try to parse the raw string
- i, ok = parseInt(t.Raw)
- if ok {
- return i
- }
- // fallback to a standard conversion
- return int64(t.Num)
- }
- }
-
- // 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
- i, ok := safeInt(t.Num)
- if ok && i >= 0 {
- return uint64(i)
- }
- // now try to parse the raw string
- u, ok := parseUint(t.Raw)
- if ok {
- return u
- }
- // fallback to a standard conversion
- return uint64(t.Num)
- }
- }
-
- // 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 null value or is non-existent, 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.IsArray() {
- 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 obj bool
- var i int
- var key, value Result
- for ; i < len(json); i++ {
- if json[i] == '{' {
- i++
- key.Type = String
- obj = true
- break
- } else if json[i] == '[' {
- i++
- key.Type = Number
- key.Num = -1
- break
- }
- if json[i] > ' ' {
- return
- }
- }
- var str string
- var vesc bool
- var ok bool
- var idx int
- for ; i < len(json); i++ {
- if obj {
- 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 + t.Index
- } else {
- key.Num += 1
- }
- 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
- }
- if t.Indexes != nil {
- if idx < len(t.Indexes) {
- value.Index = t.Indexes[idx]
- }
- } else {
- value.Index = s + t.Index
- }
- if !iterator(key, value) {
- return
- }
- idx++
- }
- }
-
- // Map returns back a map of values. The result should be a JSON object.
- // If the result is not a JSON object, the return value will be an empty map.
- 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 {
- r := Get(t.Raw, path)
- if r.Indexes != nil {
- for i := 0; i < len(r.Indexes); i++ {
- r.Indexes[i] += t.Index
- }
- } else {
- r.Index += t.Index
- }
- return r
- }
-
- 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
- }
- value.Index = i + t.Index
-
- 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:
- if t.Indexes != nil {
- if len(t.Indexes) != len(r.a) {
- for i := 0; i < len(r.a); i++ {
- r.a[i].Index = 0
- }
- } else {
- for i := 0; i < len(r.a); i++ {
- r.a[i].Index = t.Indexes[i]
- }
- }
- }
- 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
- i := 0
- for ; 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] {
- case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'i', 'I', 'N':
- value.Type = Number
- value.Raw, value.Num = tonum(json[i:])
- case 'n':
- if i+1 < len(json) && json[i+1] != 'u' {
- // nan
- value.Type = Number
- value.Raw, value.Num = tonum(json[i:])
- } else {
- // null
- 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:])
- default:
- return Result{}
- }
- break
- }
- if value.Exists() {
- value.Index = i
- }
- 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 '{' or '(' or '"'
- // squash the value, ignoring all nested arrays and objects.
- var i, depth int
- if json[0] != '"' {
- i, depth = 1, 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
- }
- }
- if depth == 0 {
- if i >= len(json) {
- return json
- }
- return json[:i+1]
- }
- 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.
- } else if json[i] == ']' || json[i] == '}' {
- // break on ']' 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
- }
- }
- return json[:i+1], unescape(json[1:i])
- }
- }
- 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
- all bool
- path string
- op string
- value string
- }
- }
-
- 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]
- if !r.arrch && i < len(path)-1 && isDotPiperChar(path[i+1]) {
- r.pipe = path[i+1:]
- r.piped = true
- } else {
- 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
- qpath, op, value, _, fi, vesc, ok :=
- parseQuery(path[i:])
- if !ok {
- // bad query, end now
- break
- }
- if len(value) >= 2 && value[0] == '"' &&
- value[len(value)-1] == '"' {
- value = value[1 : len(value)-1]
- if vesc {
- value = unescape(value)
- }
- }
- 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
- }
- }
- }
- 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, vesc, ok bool,
- ) {
- if len(query) < 2 || query[0] != '#' ||
- (query[1] != '(' && query[1] != '[') {
- return "", "", "", "", i, false, 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] == '\\' {
- vesc = true
- i++
- } else if query[i] == '"' {
- break
- }
- }
- }
- }
- if depth > 0 {
- return "", "", "", "", i, false, 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, vesc, 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
- }
-
- // peek at the next byte and see if it's a '@', '[', or '{'.
- func isDotPiperChar(c byte) bool {
- return !DisableModifiers && (c == '@' || c == '[' || c == '{')
- }
-
- 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] == '.' {
- r.part = path[:i]
- if i < len(path)-1 && isDotPiperChar(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)
- if i < len(path)-1 && isDotPiperChar(path[i+1]) {
- r.pipe = path[i+1:]
- r.piped = true
- } else {
- r.path = path[i+1:]
- }
- 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 '{' or '('
- // squash the value, ignoring all nested arrays and objects.
- // the first '[' or '{' 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 = matchLimit(unescape(key), rp.part)
- } else {
- pmatch = matchLimit(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++ {
- var num bool
- 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 'n':
- if i+1 < len(c.json) && c.json[i+1] != 'u' {
- num = true
- break
- }
- fallthrough
- case 't', 'f':
- 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
- }
- case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'i', 'I', 'N':
- num = true
- }
- if num {
- 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
- }
- }
- break
- }
- }
- return i, false
- }
-
- // matchLimit will limit the complexity of the match operation to avoid ReDos
- // attacks from arbritary inputs.
- // See the github.com/tidwall/match.MatchLimit function for more information.
- func matchLimit(str, pattern string) bool {
- matched, _ := match.MatchLimit(str, pattern, 10000)
- return matched
- }
-
- func queryMatches(rp *arrayPathResult, value Result) bool {
- rpv := rp.query.value
- if len(rpv) > 0 && rpv[0] == '~' {
- // convert to bool
- rpv = rpv[1:]
- if value.Bool() {
- value = Result{Type: True}
- } else {
- value = Result{Type: False}
- }
- }
- 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 matchLimit(value.Str, rpv)
- case "!%":
- return !matchLimit(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
- var queryIndexes []int
- 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 tmp parseContext
- tmp.value = qval
- fillIndex(c.json, &tmp)
- parentIndex := tmp.value.Index
- 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...)
- queryIndexes = append(queryIndexes, res.Index+parentIndex)
- }
- } 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]
- }
- var num bool
- 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 'n':
- if i+1 < len(c.json) && c.json[i+1] != 'u' {
- num = true
- break
- }
- fallthrough
- case 't', 'f':
- 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 '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'i', 'I', 'N':
- num = 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 indexes = make([]int, 0, 64)
- var jsons = make([]byte, 0, 64)
- jsons = append(jsons, '[')
- for j, k := 0, 0; j < len(alog); j++ {
- idx := alog[j]
- for idx < len(c.json) {
- switch c.json[idx] {
- case ' ', '\t', '\r', '\n':
- idx++
- continue
- }
- break
- }
- if idx < len(c.json) && c.json[idx] != ']' {
- _, res, ok := parseAny(c.json, idx, 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)...)
- indexes = append(indexes, res.Index)
- k++
- }
- }
- }
- }
- jsons = append(jsons, ']')
- c.value.Type = JSON
- c.value.Raw = string(jsons)
- c.value.Indexes = indexes
- 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 !c.value.Exists() {
- if len(multires) > 0 {
- c.value = Result{
- Raw: string(append(multires, ']')),
- Type: JSON,
- Indexes: queryIndexes,
- }
- } else if rp.query.all {
- c.value = Result{
- Raw: "[]",
- Type: JSON,
- }
- }
- }
- return i + 1, false
- }
- if num {
- 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
- }
- }
- 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
- }
-
- if len(path) > 0 && path[0] == '{' {
- squashed := squash(path[1:])
- if len(squashed) < len(path)-1 {
- squashed = path[:len(squashed)+1]
- remain := path[len(squashed):]
- if remain[0] == '|' {
- return squashed, remain[1:], true
- }
- }
- 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) {
- modifier := 0
- 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
- modifier = 0
- start = i + 1
- }
- for ; i < len(path); i++ {
- switch path[i] {
- case '\\':
- i++
- case '@':
- if modifier == 0 && i > 0 && (path[i-1] == '.' || path[i-1] == '|') {
- modifier = i
- }
- case ':':
- if modifier == 0 && colon == 0 && 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 separated 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 (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)
- } 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] == '{' {
- // 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 {
- 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
- }
- var tmp parseContext
- tmp.value = res
- fillIndex(json, &tmp)
- return i, tmp.value, true
- }
- if json[i] <= ' ' {
- continue
- }
- var num bool
- 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 'n':
- if i+1 < len(json) && json[i+1] != 'u' {
- num = true
- break
- }
- fallthrough
- case 't', 'f':
- 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
- }
- case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'i', 'I', 'N':
- num = true
- }
- if num {
- i, val = parseNumber(json, i)
- if hit {
- res.Raw = val
- res.Type = Number
- res.Num, _ = strconv.ParseFloat(val, 64)
- }
- return i, res, true
- }
-
- }
- return i, res, false
- }
-
- // 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
- }
-
- 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++
- if i == len(data) {
- return i, false
- }
- if data[i] < '0' || data[i] > '9' {
- return i, false
- }
- }
- // 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
- }
-
- // safeInt validates a given JSON number
- // ensures it lies within the minimum and maximum representable JSON numbers
- func safeInt(f float64) (n int64, ok bool) {
- // https://tc39.es/ecma262/#sec-number.min_safe_integer
- // https://tc39.es/ecma262/#sec-number.max_safe_integer
- if f < -9007199254740991 || f > 9007199254740991 {
- return 0, false
- }
- 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.
- // 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
- 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 = squash(pathOut)
- 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
- }
-
- // unwrap removes the '[]' or '{}' characters around json
- func unwrap(json string) string {
- json = trim(json)
- if len(json) >= 2 && (json[0] == '[' || json[0] == '{') {
- json = json[1 : len(json)-1]
- }
- return json
- }
-
- // DisableModifiers will disable the modifier syntax
- var DisableModifiers = false
-
- var modifiers = map[string]func(json, arg string) string{
- "pretty": modPretty,
- "ugly": modUgly,
- "reverse": modReverse,
- "this": modThis,
- "flatten": modFlatten,
- "join": modJoin,
- "valid": modValid,
- "keys": modKeys,
- "values": modValues,
- }
-
- // 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
- }
-
- // cleanWS remove any non-whitespace from string
- func cleanWS(s string) string {
- for i := 0; i < len(s); i++ {
- switch s[i] {
- case ' ', '\t', '\n', '\r':
- continue
- default:
- var s2 []byte
- for i := 0; i < len(s); i++ {
- switch s[i] {
- case ' ', '\t', '\n', '\r':
- s2 = append(s2, s[i])
- }
- }
- return string(s2)
- }
- }
- return s
- }
-
- // @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 = cleanWS(value.String())
- case "prefix":
- opts.Prefix = cleanWS(value.String())
- case "width":
- opts.Width = int(value.Int())
- }
- return true
- })
- return bytesString(pretty.PrettyOptions(stringBytes(json), &opts))
- }
- return bytesString(pretty.Pretty(stringBytes(json)))
- }
-
- // @this returns the current element. Can be used to retrieve the root element.
- func modThis(json, arg string) string {
- return 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
- }
-
- // @flatten an array with child arrays.
- // [1,[2],[3,4],[5,[6,7]]] -> [1,2,3,4,5,[6,7]]
- // The {"deep":true} arg can be provide for deep flattening.
- // [1,[2],[3,4],[5,[6,7]]] -> [1,2,3,4,5,6,7]
- // The original json is returned when the json is not an array.
- func modFlatten(json, arg string) string {
- res := Parse(json)
- if !res.IsArray() {
- return json
- }
- var deep bool
- if arg != "" {
- Parse(arg).ForEach(func(key, value Result) bool {
- if key.String() == "deep" {
- deep = value.Bool()
- }
- return true
- })
- }
- var out []byte
- out = append(out, '[')
- var idx int
- res.ForEach(func(_, value Result) bool {
- var raw string
- if value.IsArray() {
- if deep {
- raw = unwrap(modFlatten(value.Raw, arg))
- } else {
- raw = unwrap(value.Raw)
- }
- } else {
- raw = value.Raw
- }
- raw = strings.TrimSpace(raw)
- if len(raw) > 0 {
- if idx > 0 {
- out = append(out, ',')
- }
- out = append(out, raw...)
- idx++
- }
- return true
- })
- out = append(out, ']')
- return bytesString(out)
- }
-
- // @keys extracts the keys from an object.
- // {"first":"Tom","last":"Smith"} -> ["first","last"]
- func modKeys(json, arg string) string {
- v := Parse(json)
- if !v.Exists() {
- return "[]"
- }
- obj := v.IsObject()
- var out strings.Builder
- out.WriteByte('[')
- var i int
- v.ForEach(func(key, _ Result) bool {
- if i > 0 {
- out.WriteByte(',')
- }
- if obj {
- out.WriteString(key.Raw)
- } else {
- out.WriteString("null")
- }
- i++
- return true
- })
- out.WriteByte(']')
- return out.String()
- }
-
- // @values extracts the values from an object.
- // {"first":"Tom","last":"Smith"} -> ["Tom","Smith"]
- func modValues(json, arg string) string {
- v := Parse(json)
- if !v.Exists() {
- return "[]"
- }
- if v.IsArray() {
- return json
- }
- var out strings.Builder
- out.WriteByte('[')
- var i int
- v.ForEach(func(_, value Result) bool {
- if i > 0 {
- out.WriteByte(',')
- }
- out.WriteString(value.Raw)
- i++
- return true
- })
- out.WriteByte(']')
- return out.String()
- }
-
- // @join multiple objects into a single object.
- // [{"first":"Tom"},{"last":"Smith"}] -> {"first","Tom","last":"Smith"}
- // The arg can be "true" to specify that duplicate keys should be preserved.
- // [{"first":"Tom","age":37},{"age":41}] -> {"first","Tom","age":37,"age":41}
- // Without preserved keys:
- // [{"first":"Tom","age":37},{"age":41}] -> {"first","Tom","age":41}
- // The original json is returned when the json is not an object.
- func modJoin(json, arg string) string {
- res := Parse(json)
- if !res.IsArray() {
- return json
- }
- var preserve bool
- if arg != "" {
- Parse(arg).ForEach(func(key, value Result) bool {
- if key.String() == "preserve" {
- preserve = value.Bool()
- }
- return true
- })
- }
- var out []byte
- out = append(out, '{')
- if preserve {
- // Preserve duplicate keys.
- var idx int
- res.ForEach(func(_, value Result) bool {
- if !value.IsObject() {
- return true
- }
- if idx > 0 {
- out = append(out, ',')
- }
- out = append(out, unwrap(value.Raw)...)
- idx++
- return true
- })
- } else {
- // Deduplicate keys and generate an object with stable ordering.
- var keys []Result
- kvals := make(map[string]Result)
- res.ForEach(func(_, value Result) bool {
- if !value.IsObject() {
- return true
- }
- value.ForEach(func(key, value Result) bool {
- k := key.String()
- if _, ok := kvals[k]; !ok {
- keys = append(keys, key)
- }
- kvals[k] = value
- return true
- })
- return true
- })
- for i := 0; i < len(keys); i++ {
- if i > 0 {
- out = append(out, ',')
- }
- out = append(out, keys[i].Raw...)
- out = append(out, ':')
- out = append(out, kvals[keys[i].String()].Raw...)
- }
- }
- out = append(out, '}')
- return bytesString(out)
- }
-
- // @valid ensures that the json is valid before moving on. An empty string is
- // returned when the json is not valid, otherwise it returns the original json.
- func modValid(json, arg string) string {
- if !Valid(json) {
- return ""
- }
- return json
- }
-
- // stringHeader instead of reflect.StringHeader
- type stringHeader struct {
- data unsafe.Pointer
- len int
- }
-
- // sliceHeader instead of reflect.SliceHeader
- type sliceHeader struct {
- data unsafe.Pointer
- len int
- cap int
- }
-
- // getBytes casts the input json bytes to a string and safely returns the
- // results as uniquely allocated data. This operation is intended to minimize
- // copies and allocations for the large json string->[]byte.
- func getBytes(json []byte, path string) Result {
- var result Result
- if json != nil {
- // unsafe cast to string
- result = Get(*(*string)(unsafe.Pointer(&json)), path)
- // safely get the string headers
- rawhi := *(*stringHeader)(unsafe.Pointer(&result.Raw))
- strhi := *(*stringHeader)(unsafe.Pointer(&result.Str))
- // create byte slice headers
- rawh := sliceHeader{data: rawhi.data, len: rawhi.len, cap: rawhi.len}
- strh := sliceHeader{data: strhi.data, len: strhi.len, cap: rawhi.len}
- if strh.data == nil {
- // str is nil
- if rawh.data == nil {
- // raw is nil
- result.Raw = ""
- } else {
- // raw has data, safely copy the slice header to a string
- result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))
- }
- result.Str = ""
- } else if rawh.data == nil {
- // raw is nil
- result.Raw = ""
- // str has data, safely copy the slice header to a string
- result.Str = string(*(*[]byte)(unsafe.Pointer(&strh)))
- } else if uintptr(strh.data) >= uintptr(rawh.data) &&
- uintptr(strh.data)+uintptr(strh.len) <=
- uintptr(rawh.data)+uintptr(rawh.len) {
- // Str is a substring of Raw.
- start := uintptr(strh.data) - uintptr(rawh.data)
- // safely copy the raw slice header
- result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))
- // substring the raw
- result.Str = result.Raw[start : start+uintptr(strh.len)]
- } else {
- // safely copy both the raw and str slice headers to strings
- result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))
- result.Str = string(*(*[]byte)(unsafe.Pointer(&strh)))
- }
- }
- return result
- }
-
- // fillIndex finds the position of Raw data and assigns it to the Index field
- // of the resulting value. If the position cannot be found then Index zero is
- // used instead.
- func fillIndex(json string, c *parseContext) {
- if len(c.value.Raw) > 0 && !c.calcd {
- jhdr := *(*stringHeader)(unsafe.Pointer(&json))
- rhdr := *(*stringHeader)(unsafe.Pointer(&(c.value.Raw)))
- c.value.Index = int(uintptr(rhdr.data) - uintptr(jhdr.data))
- if c.value.Index < 0 || c.value.Index >= len(json) {
- c.value.Index = 0
- }
- }
- }
-
- func stringBytes(s string) []byte {
- return *(*[]byte)(unsafe.Pointer(&sliceHeader{
- data: (*stringHeader)(unsafe.Pointer(&s)).data,
- len: len(s),
- cap: len(s),
- }))
- }
-
- func bytesString(b []byte) string {
- return *(*string)(unsafe.Pointer(&b))
- }
-
- func revSquash(json string) string {
- // reverse squash
- // expects that the tail character is a ']' or '}' or ')' or '"'
- // squash the value, ignoring all nested arrays and objects.
- i := len(json) - 1
- var depth int
- if json[i] != '"' {
- depth++
- }
- if json[i] == '}' || json[i] == ']' || json[i] == ')' {
- i--
- }
- for ; i >= 0; i-- {
- switch json[i] {
- case '"':
- i--
- for ; i >= 0; i-- {
- if json[i] == '"' {
- esc := 0
- for i > 0 && json[i-1] == '\\' {
- i--
- esc++
- }
- if esc%2 == 1 {
- continue
- }
- i += esc
- break
- }
- }
- if depth == 0 {
- if i < 0 {
- i = 0
- }
- return json[i:]
- }
- case '}', ']', ')':
- depth++
- case '{', '[', '(':
- depth--
- if depth == 0 {
- return json[i:]
- }
- }
- }
- return json
- }
-
- func (t Result) Paths(json string) []string {
- if t.Indexes == nil {
- return nil
- }
- paths := make([]string, 0, len(t.Indexes))
- t.ForEach(func(_, value Result) bool {
- paths = append(paths, value.Path(json))
- return true
- })
- if len(paths) != len(t.Indexes) {
- return nil
- }
- return paths
- }
-
- // Path returns the original GJSON path for Result.
- // The json param must be the original JSON used when calling Get.
- func (t Result) Path(json string) string {
- var path []byte
- var comps []string // raw components
- i := t.Index - 1
- if t.Index+len(t.Raw) > len(json) {
- // JSON cannot safely contain Result.
- goto fail
- }
- if !strings.HasPrefix(json[t.Index:], t.Raw) {
- // Result is not at the JSON index as exepcted.
- goto fail
- }
- for ; i >= 0; i-- {
- if json[i] <= ' ' {
- continue
- }
- if json[i] == ':' {
- // inside of object, get the key
- for ; i >= 0; i-- {
- if json[i] != '"' {
- continue
- }
- break
- }
- raw := revSquash(json[:i+1])
- i = i - len(raw)
- comps = append(comps, raw)
- // key gotten, now squash the rest
- raw = revSquash(json[:i+1])
- i = i - len(raw)
- i++ // increment the index for next loop step
- } else if json[i] == '{' {
- // Encountered an open object. The original result was probably an
- // object key.
- goto fail
- } else if json[i] == ',' || json[i] == '[' {
- // inside of an array, count the position
- var arrIdx int
- if json[i] == ',' {
- arrIdx++
- i--
- }
- for ; i >= 0; i-- {
- if json[i] == ':' {
- // Encountered an unexpected colon. The original result was
- // probably an object key.
- goto fail
- } else if json[i] == ',' {
- arrIdx++
- } else if json[i] == '[' {
- comps = append(comps, strconv.Itoa(arrIdx))
- break
- } else if json[i] == ']' || json[i] == '}' || json[i] == '"' {
- raw := revSquash(json[:i+1])
- i = i - len(raw) + 1
- }
- }
- }
- }
- if len(comps) == 0 {
- if DisableModifiers {
- goto fail
- }
- return "@this"
- }
- for i := len(comps) - 1; i >= 0; i-- {
- rcomp := Parse(comps[i])
- if !rcomp.Exists() {
- goto fail
- }
- comp := escapeComp(rcomp.String())
- path = append(path, '.')
- path = append(path, comp...)
- }
- if len(path) > 0 {
- path = path[1:]
- }
- return string(path)
- fail:
- return ""
- }
-
- // isSafePathKeyChar returns true if the input character is safe for not
- // needing escaping.
- func isSafePathKeyChar(c byte) bool {
- return c <= ' ' || c > '~' || c == '_' || c == '-' || c == ':' ||
- (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
- (c >= '0' && c <= '9')
- }
-
- // escapeComp escaped a path compontent, making it safe for generating a
- // path for later use.
- func escapeComp(comp string) string {
- for i := 0; i < len(comp); i++ {
- if !isSafePathKeyChar(comp[i]) {
- ncomp := []byte(comp[:i])
- for ; i < len(comp); i++ {
- if !isSafePathKeyChar(comp[i]) {
- ncomp = append(ncomp, '\\')
- }
- ncomp = append(ncomp, comp[i])
- }
- return string(ncomp)
- }
- }
- return comp
- }
|