12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425 |
- // Package gjson provides searching for json strings.
- package yu_json
- import (
- yu_fast "gogs.qqck.cn/s/tools/fast"
- yu_match "gogs.qqck.cn/s/tools/json/internal/match"
- yu_pretty "gogs.qqck.cn/s/tools/json/internal/pretty"
- yu_strconv "gogs.qqck.cn/s/tools/strconv"
- "strconv"
- "strings"
- "time"
- "unicode/utf16"
- "unicode/utf8"
- "unsafe"
- )
- // 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(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] == '['
- }
- // IsBool returns true if the result value is a JSON boolean.
- func (t Result) IsBool() bool {
- return t.Type == True || t.Type == False
- }
- // 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(yu_fast.B2S(data))
- func ParseBytes(json []byte) Result {
- return Parse(yu_fast.B2S(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(s string) bool {
- if DisableModifiers {
- return false
- }
- c := s[0]
- if c == '@' {
- // check that the next component is *not* a modifier.
- i := 1
- for ; i < len(s); i++ {
- if s[i] == '.' || s[i] == '|' || s[i] == ':' {
- break
- }
- }
- _, ok := modifiers[s[1:i]]
- return ok
- }
- return 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, _ := yu_match.MatchLimit(str, pattern, 10000)
- return matched
- }
- func falseish(t Result) bool {
- switch t.Type {
- case Null:
- return true
- case False:
- return true
- case String:
- b, err := strconv.ParseBool(t.Str)
- if err != nil {
- return false
- }
- return !b
- case Number:
- return t.Num == 0
- default:
- return false
- }
- }
- func trueish(t Result) bool {
- switch t.Type {
- case True:
- return true
- case String:
- b, err := strconv.ParseBool(t.Str)
- if err != nil {
- return false
- }
- return b
- case Number:
- return t.Num != 0
- default:
- return false
- }
- }
- func nullish(t Result) bool {
- return t.Type == Null
- }
- func queryMatches(rp *arrayPathResult, value Result) bool {
- rpv := rp.query.value
- if len(rpv) > 0 {
- if rpv[0] == '~' {
- // convert to bool
- rpv = rpv[1:]
- var ish, ok bool
- switch rpv {
- case "*":
- ish, ok = value.Exists(), true
- case "null":
- ish, ok = nullish(value), true
- case "true":
- ish, ok = trueish(value), true
- case "false":
- ish, ok = falseish(value), true
- }
- if ok {
- rpv = "true"
- if ish {
- value = Result{Type: True}
- } else {
- value = Result{Type: False}
- }
- } else {
- rpv = ""
- value = Result{}
- }
- }
- }
- 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, yu_fast.S2B(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 = yu_strconv.FormatInt(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
- }
- var hexchars = [...]byte{
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'a', 'b', 'c', 'd', 'e', 'f',
- }
- func appendHex16(dst []byte, x uint16) []byte {
- return append(dst,
- hexchars[x>>12&0xF], hexchars[x>>8&0xF],
- hexchars[x>>4&0xF], hexchars[x>>0&0xF],
- )
- }
- // AppendJSONString is a convenience function that converts the provided string
- // to a valid JSON string and appends it to dst.
- func AppendJSONString(dst []byte, s string) []byte {
- dst = append(dst, make([]byte, len(s)+2)...)
- dst = append(dst[:len(dst)-len(s)-2], '"')
- for i := 0; i < len(s); i++ {
- if s[i] < ' ' {
- dst = append(dst, '\\')
- switch s[i] {
- case '\n':
- dst = append(dst, 'n')
- case '\r':
- dst = append(dst, 'r')
- case '\t':
- dst = append(dst, 't')
- default:
- dst = append(dst, 'u')
- dst = appendHex16(dst, uint16(s[i]))
- }
- } else if s[i] == '>' || s[i] == '<' || s[i] == '&' {
- dst = append(dst, '\\', 'u')
- dst = appendHex16(dst, uint16(s[i]))
- } else if s[i] == '\\' {
- dst = append(dst, '\\', '\\')
- } else if s[i] == '"' {
- dst = append(dst, '\\', '"')
- } else if s[i] > 127 {
- // read utf8 character
- r, n := utf8.DecodeRuneInString(s[i:])
- if n == 0 {
- break
- }
- if r == utf8.RuneError && n == 1 {
- dst = append(dst, `\ufffd`...)
- } else if r == '\u2028' || r == '\u2029' {
- dst = append(dst, `\u202`...)
- dst = append(dst, hexchars[r&0xF])
- } else {
- dst = append(dst, s[i:i+n]...)
- }
- i = i + n - 1
- } else {
- dst = append(dst, s[i])
- }
- }
- return append(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 Get(yu_fast.B2S(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(yu_fast.S2B(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 '{', '[', '"':
- // json arg
- res := Parse(pathOut)
- if res.Exists() {
- args = squash(pathOut)
- pathOut = pathOut[len(args):]
- parsedArgs = true
- }
- }
- if !parsedArgs {
- // simple arg
- i := 0
- for ; i < len(pathOut); i++ {
- if pathOut[i] == '|' {
- break
- }
- switch pathOut[i] {
- case '{', '[', '"', '(':
- s := squash(pathOut[i:])
- i += len(s) - 1
- }
- }
- args = pathOut[:i]
- pathOut = pathOut[i:]
- }
- }
- 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
- func init() {
- 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,
- "tostr": modToStr,
- "fromstr": modFromStr,
- "group": modGroup,
- "dig": modDig,
- }
- }
- // 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 := *yu_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 string(yu_pretty.PrettyOptions(yu_fast.S2B(json), &opts))
- }
- return string(yu_pretty.Pretty(yu_fast.S2B(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 string(yu_pretty.Ugly(yu_fast.S2B(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 string(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 string(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 string(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 string(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
- }
- // @fromstr converts a string to json
- //
- // "{\"id\":1023,\"name\":\"alert\"}" -> {"id":1023,"name":"alert"}
- func modFromStr(json, arg string) string {
- if !Valid(json) {
- return ""
- }
- return Parse(json).String()
- }
- // @tostr converts a string to json
- //
- // {"id":1023,"name":"alert"} -> "{\"id\":1023,\"name\":\"alert\"}"
- func modToStr(str, arg string) string {
- return string(AppendJSONString(nil, str))
- }
- func modGroup(json, arg string) string {
- res := Parse(json)
- if !res.IsObject() {
- return ""
- }
- var all [][]byte
- res.ForEach(func(key, value Result) bool {
- if !value.IsArray() {
- return true
- }
- var idx int
- value.ForEach(func(_, value Result) bool {
- if idx == len(all) {
- all = append(all, []byte{})
- }
- all[idx] = append(all[idx], ("," + key.Raw + ":" + value.Raw)...)
- idx++
- return true
- })
- return true
- })
- var data []byte
- data = append(data, '[')
- for i, item := range all {
- if i > 0 {
- data = append(data, ',')
- }
- data = append(data, '{')
- data = append(data, item[1:]...)
- data = append(data, '}')
- }
- data = append(data, ']')
- return string(data)
- }
- // stringHeader instead of reflect.StringHeader
- type stringHeader struct {
- data unsafe.Pointer
- len int
- }
- // 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 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
- }
- // Paths returns the original GJSON paths for a Result where the Result came
- // from a simple query path that returns an array, like:
- //
- // gjson.Get(json, "friends.#.first")
- //
- // The returned value will be in the form of a JSON array:
- //
- // ["friends.0.first","friends.1.first","friends.2.first"]
- //
- // The param 'json' must be the original JSON used when calling Get.
- //
- // Returns an empty string if the paths cannot be determined, which can happen
- // when the Result came from a path that contained a multipath, modifier,
- // or a nested query.
- 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 a Result where the Result came
- // from a simple path that returns a single value, like:
- //
- // gjson.Get(json, "friends.#(last=Murphy)")
- //
- // The returned value will be in the form of a JSON string:
- //
- // "friends.0"
- //
- // The param 'json' must be the original JSON used when calling Get.
- //
- // Returns an empty string if the paths cannot be determined, which can happen
- // when the Result came from a path that contained a multipath, modifier,
- // or a nested query.
- 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, yu_strconv.FormatInt(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 := Escape(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 >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
- (c >= '0' && c <= '9') || c <= ' ' || c > '~' || c == '_' ||
- c == '-' || c == ':'
- }
- // Escape returns an escaped path component.
- //
- // json := `{
- // "user":{
- // "first.name": "Janet",
- // "last.name": "Prichard"
- // }
- // }`
- // user := gjson.Get(json, "user")
- // println(user.Get(gjson.Escape("first.name"))
- // println(user.Get(gjson.Escape("last.name"))
- // // Output:
- // // Janet
- // // Prichard
- func Escape(comp string) string {
- for i := 0; i < len(comp); i++ {
- if !isSafePathKeyChar(comp[i]) {
- ncomp := make([]byte, len(comp)+1)
- copy(ncomp, comp[:i])
- ncomp = ncomp[:i]
- for ; i < len(comp); i++ {
- if !isSafePathKeyChar(comp[i]) {
- ncomp = append(ncomp, '\\')
- }
- ncomp = append(ncomp, comp[i])
- }
- return string(ncomp)
- }
- }
- return comp
- }
- func parseRecursiveDescent(all []Result, parent Result, path string) []Result {
- if res := parent.Get(path); res.Exists() {
- all = append(all, res)
- }
- if parent.IsArray() || parent.IsObject() {
- parent.ForEach(func(_, val Result) bool {
- all = parseRecursiveDescent(all, val, path)
- return true
- })
- }
- return all
- }
- func modDig(json, arg string) string {
- all := parseRecursiveDescent(nil, Parse(json), arg)
- var out []byte
- out = append(out, '[')
- for i, res := range all {
- if i > 0 {
- out = append(out, ',')
- }
- out = append(out, res.Raw...)
- }
- out = append(out, ']')
- return string(out)
- }
|