on c2fc795 help sign in
1283 lines 30 kB history raw
1package jsonparser
3import (
4 "bytes"
5 "errors"
6 "fmt"
7 "strconv"
8)
10// Errors
11var (
12 KeyPathNotFoundError = errors.New("Key path not found")
13 UnknownValueTypeError = errors.New("Unknown value type")
14 MalformedJsonError = errors.New("Malformed JSON error")
15 MalformedStringError = errors.New("Value is string, but can't find closing '\"' symbol")
16 MalformedArrayError = errors.New("Value is array, but can't find closing ']' symbol")
17 MalformedObjectError = errors.New("Value looks like object, but can't find closing '}' symbol")
18 MalformedValueError = errors.New("Value looks like Number/Boolean/None, but can't find its end: ',' or '}' symbol")
19 OverflowIntegerError = errors.New("Value is number, but overflowed while parsing")
20 MalformedStringEscapeError = errors.New("Encountered an invalid escape sequence in a string")
23// How much stack space to allocate for unescaping JSON strings; if a string longer
24// than this needs to be escaped, it will result in a heap allocation
25const unescapeStackBufSize = 64
27func tokenEnd(data []byte) int {
28 for i, c := range data {
29 switch c {
30 case ' ', '\n', '\r', '\t', ',', '}', ']':
31 return i
32 }
33 }
35 return len(data)
38func findTokenStart(data []byte, token byte) int {
39 for i := len(data) - 1; i >= 0; i-- {
40 switch data[i] {
41 case token:
42 return i
43 case '[', '{':
44 return 0
45 }
46 }
48 return 0
51func findKeyStart(data []byte, key string) (int, error) {
52 i := 0
53 ln := len(data)
54 if ln > 0 && (data[0] == '{' || data[0] == '[') {
55 i = 1
56 }
57 var stackbuf [unescapeStackBufSize]byte // stack-allocated array for allocation-free unescaping of small strings
59 if ku, err := Unescape(StringToBytes(key), stackbuf[:]); err == nil {
60 key = bytesToString(&ku)
61 }
63 for i < ln {
64 switch data[i] {
65 case '"':
66 i++
67 keyBegin := i
69 strEnd, keyEscaped := stringEnd(data[i:])
70 if strEnd == -1 {
71 break
72 }
73 i += strEnd
74 keyEnd := i - 1
76 valueOffset := nextToken(data[i:])
77 if valueOffset == -1 {
78 break
79 }
81 i += valueOffset
83 // if string is a key, and key level match
84 k := data[keyBegin:keyEnd]
85 // for unescape: if there are no escape sequences, this is cheap; if there are, it is a
86 // bit more expensive, but causes no allocations unless len(key) > unescapeStackBufSize
87 if keyEscaped {
88 if ku, err := Unescape(k, stackbuf[:]); err != nil {
89 break
90 } else {
91 k = ku
92 }
93 }
95 if data[i] == ':' && len(key) == len(k) && bytesToString(&k) == key {
96 return keyBegin - 1, nil
97 }
99 case '[':
100 end := blockEnd(data[i:], data[i], ']')
101 if end != -1 {
102 i = i + end
103 }
104 case '{':
105 end := blockEnd(data[i:], data[i], '}')
106 if end != -1 {
107 i = i + end
108 }
109 }
110 i++
111 }
113 return -1, KeyPathNotFoundError
116func tokenStart(data []byte) int {
117 for i := len(data) - 1; i >= 0; i-- {
118 switch data[i] {
119 case '\n', '\r', '\t', ',', '{', '[':
120 return i
121 }
122 }
124 return 0
127// Find position of next character which is not whitespace
128func nextToken(data []byte) int {
129 for i, c := range data {
130 switch c {
131 case ' ', '\n', '\r', '\t':
132 continue
133 default:
134 return i
135 }
136 }
138 return -1
141// Find position of last character which is not whitespace
142func lastToken(data []byte) int {
143 for i := len(data) - 1; i >= 0; i-- {
144 switch data[i] {
145 case ' ', '\n', '\r', '\t':
146 continue
147 default:
148 return i
149 }
150 }
152 return -1
155// Tries to find the end of string
156// Support if string contains escaped quote symbols.
157func stringEnd(data []byte) (int, bool) {
158 escaped := false
159 for i, c := range data {
160 if c == '"' {
161 if !escaped {
162 return i + 1, false
163 } else {
164 j := i - 1
165 for {
166 if j < 0 || data[j] != '\\' {
167 return i + 1, true // even number of backslashes
168 }
169 j--
170 if j < 0 || data[j] != '\\' {
171 break // odd number of backslashes
172 }
173 j--
175 }
176 }
177 } else if c == '\\' {
178 escaped = true
179 }
180 }
182 return -1, escaped
185// Find end of the data structure, array or object.
186// For array openSym and closeSym will be '[' and ']', for object '{' and '}'
187func blockEnd(data []byte, openSym byte, closeSym byte) int {
188 level := 0
189 i := 0
190 ln := len(data)
192 for i < ln {
193 switch data[i] {
194 case '"': // If inside string, skip it
195 se, _ := stringEnd(data[i+1:])
196 if se == -1 {
197 return -1
198 }
199 i += se
200 case openSym: // If open symbol, increase level
201 level++
202 case closeSym: // If close symbol, increase level
203 level--
205 // If we have returned to the original level, we're done
206 if level == 0 {
207 return i + 1
208 }
209 }
210 i++
211 }
213 return -1
216func searchKeys(data []byte, keys ...string) int {
217 keyLevel := 0
218 level := 0
219 i := 0
220 ln := len(data)
221 lk := len(keys)
222 lastMatched := true
224 if lk == 0 {
225 return 0
226 }
228 var stackbuf [unescapeStackBufSize]byte // stack-allocated array for allocation-free unescaping of small strings
230 for i < ln {
231 switch data[i] {
232 case '"':
233 i++
234 keyBegin := i
236 strEnd, keyEscaped := stringEnd(data[i:])
237 if strEnd == -1 {
238 return -1
239 }
240 i += strEnd
241 keyEnd := i - 1
243 valueOffset := nextToken(data[i:])
244 if valueOffset == -1 {
245 return -1
246 }
248 i += valueOffset
250 // if string is a key
251 if data[i] == ':' {
252 if level < 1 {
253 return -1
254 }
256 key := data[keyBegin:keyEnd]
258 // for unescape: if there are no escape sequences, this is cheap; if there are, it is a
259 // bit more expensive, but causes no allocations unless len(key) > unescapeStackBufSize
260 var keyUnesc []byte
261 if !keyEscaped {
262 keyUnesc = key
263 } else if ku, err := Unescape(key, stackbuf[:]); err != nil {
264 return -1
265 } else {
266 keyUnesc = ku
267 }
269 if level <= len(keys) {
270 if equalStr(&keyUnesc, keys[level-1]) {
271 lastMatched = true
273 // if key level match
274 if keyLevel == level-1 {
275 keyLevel++
276 // If we found all keys in path
277 if keyLevel == lk {
278 return i + 1
279 }
280 }
281 } else {
282 lastMatched = false
283 }
284 } else {
285 return -1
286 }
287 } else {
288 i--
289 }
290 case '{':
292 // in case parent key is matched then only we will increase the level otherwise can directly
293 // can move to the end of this block
294 if !lastMatched {
295 end := blockEnd(data[i:], '{', '}')
296 if end == -1 {
297 return -1
298 }
299 i += end - 1
300 } else {
301 level++
302 }
303 case '}':
304 level--
305 if level == keyLevel {
306 keyLevel--
307 }
308 case '[':
309 // If we want to get array element by index
310 if keyLevel == level && keys[level][0] == '[' {
311 var keyLen = len(keys[level])
312 if keyLen < 3 || keys[level][0] != '[' || keys[level][keyLen-1] != ']' {
313 return -1
314 }
315 aIdx, err := strconv.Atoi(keys[level][1 : keyLen-1])
316 if err != nil {
317 return -1
318 }
319 var curIdx int
320 var valueFound []byte
321 var valueOffset int
322 var curI = i
323 ArrayEach(data[i:], func(value []byte, dataType ValueType, offset int, err error) {
324 if curIdx == aIdx {
325 valueFound = value
326 valueOffset = offset
327 if dataType == String {
328 valueOffset = valueOffset - 2
329 valueFound = data[curI+valueOffset : curI+valueOffset+len(value)+2]
330 }
331 }
332 curIdx += 1
333 })
335 if valueFound == nil {
336 return -1
337 } else {
338 subIndex := searchKeys(valueFound, keys[level+1:]...)
339 if subIndex < 0 {
340 return -1
341 }
342 return i + valueOffset + subIndex
343 }
344 } else {
345 // Do not search for keys inside arrays
346 if arraySkip := blockEnd(data[i:], '[', ']'); arraySkip == -1 {
347 return -1
348 } else {
349 i += arraySkip - 1
350 }
351 }
352 case ':': // If encountered, JSON data is malformed
353 return -1
354 }
356 i++
357 }
359 return -1
362func sameTree(p1, p2 []string) bool {
363 minLen := len(p1)
364 if len(p2) < minLen {
365 minLen = len(p2)
366 }
368 for pi_1, p_1 := range p1[:minLen] {
369 if p2[pi_1] != p_1 {
370 return false
371 }
372 }
374 return true
377func EachKey(data []byte, cb func(int, []byte, ValueType, error), paths ...[]string) int {
378 var x struct{}
379 pathFlags := make([]bool, len(paths))
380 var level, pathsMatched, i int
381 ln := len(data)
383 var maxPath int
384 for _, p := range paths {
385 if len(p) > maxPath {
386 maxPath = len(p)
387 }
388 }
390 pathsBuf := make([]string, maxPath)
392 for i < ln {
393 switch data[i] {
394 case '"':
395 i++
396 keyBegin := i
398 strEnd, keyEscaped := stringEnd(data[i:])
399 if strEnd == -1 {
400 return -1
401 }
402 i += strEnd
404 keyEnd := i - 1
406 valueOffset := nextToken(data[i:])
407 if valueOffset == -1 {
408 return -1
409 }
411 i += valueOffset
413 // if string is a key, and key level match
414 if data[i] == ':' {
415 match := -1
416 key := data[keyBegin:keyEnd]
418 // for unescape: if there are no escape sequences, this is cheap; if there are, it is a
419 // bit more expensive, but causes no allocations unless len(key) > unescapeStackBufSize
420 var keyUnesc []byte
421 if !keyEscaped {
422 keyUnesc = key
423 } else {
424 var stackbuf [unescapeStackBufSize]byte
425 if ku, err := Unescape(key, stackbuf[:]); err != nil {
426 return -1
427 } else {
428 keyUnesc = ku
429 }
430 }
432 if maxPath >= level {
433 if level < 1 {
434 cb(-1, nil, Unknown, MalformedJsonError)
435 return -1
436 }
438 pathsBuf[level-1] = bytesToString(&keyUnesc)
439 for pi, p := range paths {
440 if len(p) != level || pathFlags[pi] || !equalStr(&keyUnesc, p[level-1]) || !sameTree(p, pathsBuf[:level]) {
441 continue
442 }
444 match = pi
446 pathsMatched++
447 pathFlags[pi] = true
449 v, dt, _, e := Get(data[i+1:])
450 cb(pi, v, dt, e)
452 if pathsMatched == len(paths) {
453 break
454 }
455 }
456 if pathsMatched == len(paths) {
457 return i
458 }
459 }
461 if match == -1 {
462 tokenOffset := nextToken(data[i+1:])
463 i += tokenOffset
465 if data[i] == '{' {
466 blockSkip := blockEnd(data[i:], '{', '}')
467 i += blockSkip + 1
468 }
469 }
471 if i < ln {
472 switch data[i] {
473 case '{', '}', '[', '"':
474 i--
475 }
476 }
477 } else {
478 i--
479 }
480 case '{':
481 level++
482 case '}':
483 level--
484 case '[':
485 var ok bool
486 arrIdxFlags := make(map[int]struct{})
487 pIdxFlags := make([]bool, len(paths))
489 if level < 0 {
490 cb(-1, nil, Unknown, MalformedJsonError)
491 return -1
492 }
494 for pi, p := range paths {
495 if len(p) < level+1 || pathFlags[pi] || p[level][0] != '[' || !sameTree(p, pathsBuf[:level]) {
496 continue
497 }
498 if len(p[level]) >= 2 {
499 aIdx, _ := strconv.Atoi(p[level][1 : len(p[level])-1])
500 arrIdxFlags[aIdx] = x
501 pIdxFlags[pi] = true
502 }
503 }
505 if len(arrIdxFlags) > 0 {
506 level++
508 var curIdx int
509 arrOff, _ := ArrayEach(data[i:], func(value []byte, dataType ValueType, offset int, err error) {
510 if _, ok = arrIdxFlags[curIdx]; ok {
511 for pi, p := range paths {
512 if pIdxFlags[pi] {
513 aIdx, _ := strconv.Atoi(p[level-1][1 : len(p[level-1])-1])
515 if curIdx == aIdx {
516 of := searchKeys(value, p[level:]...)
518 pathsMatched++
519 pathFlags[pi] = true
521 if of != -1 {
522 v, dt, _, e := Get(value[of:])
523 cb(pi, v, dt, e)
524 }
525 }
526 }
527 }
528 }
530 curIdx += 1
531 })
533 if pathsMatched == len(paths) {
534 return i
535 }
537 i += arrOff - 1
538 } else {
539 // Do not search for keys inside arrays
540 if arraySkip := blockEnd(data[i:], '[', ']'); arraySkip == -1 {
541 return -1
542 } else {
543 i += arraySkip - 1
544 }
545 }
546 case ']':
547 level--
548 }
550 i++
551 }
553 return -1
556// Data types available in valid JSON data.
557type ValueType int
559const (
560 NotExist = ValueType(iota)
561 String
562 Number
563 Object
564 Array
565 Boolean
566 Null
567 Unknown
570func (vt ValueType) String() string {
571 switch vt {
572 case NotExist:
573 return "non-existent"
574 case String:
575 return "string"
576 case Number:
577 return "number"
578 case Object:
579 return "object"
580 case Array:
581 return "array"
582 case Boolean:
583 return "boolean"
584 case Null:
585 return "null"
586 default:
587 return "unknown"
588 }
591var (
592 trueLiteral = []byte("true")
593 falseLiteral = []byte("false")
594 nullLiteral = []byte("null")
597func createInsertComponent(keys []string, setValue []byte, comma, object bool) []byte {
598 isIndex := string(keys[0][0]) == "["
599 offset := 0
600 lk := calcAllocateSpace(keys, setValue, comma, object)
601 buffer := make([]byte, lk, lk)
602 if comma {
603 offset += WriteToBuffer(buffer[offset:], ",")
604 }
605 if isIndex && !comma {
606 offset += WriteToBuffer(buffer[offset:], "[")
607 } else {
608 if object {
609 offset += WriteToBuffer(buffer[offset:], "{")
610 }
611 if !isIndex {
612 offset += WriteToBuffer(buffer[offset:], "\"")
613 offset += WriteToBuffer(buffer[offset:], keys[0])
614 offset += WriteToBuffer(buffer[offset:], "\":")
615 }
616 }
618 for i := 1; i < len(keys); i++ {
619 if string(keys[i][0]) == "[" {
620 offset += WriteToBuffer(buffer[offset:], "[")
621 } else {
622 offset += WriteToBuffer(buffer[offset:], "{\"")
623 offset += WriteToBuffer(buffer[offset:], keys[i])
624 offset += WriteToBuffer(buffer[offset:], "\":")
625 }
626 }
627 offset += WriteToBuffer(buffer[offset:], string(setValue))
628 for i := len(keys) - 1; i > 0; i-- {
629 if string(keys[i][0]) == "[" {
630 offset += WriteToBuffer(buffer[offset:], "]")
631 } else {
632 offset += WriteToBuffer(buffer[offset:], "}")
633 }
634 }
635 if isIndex && !comma {
636 offset += WriteToBuffer(buffer[offset:], "]")
637 }
638 if object && !isIndex {
639 offset += WriteToBuffer(buffer[offset:], "}")
640 }
641 return buffer
644func calcAllocateSpace(keys []string, setValue []byte, comma, object bool) int {
645 isIndex := string(keys[0][0]) == "["
646 lk := 0
647 if comma {
648 // ,
649 lk += 1
650 }
651 if isIndex && !comma {
652 // []
653 lk += 2
654 } else {
655 if object {
656 // {
657 lk += 1
658 }
659 if !isIndex {
660 // "keys[0]"
661 lk += len(keys[0]) + 3
662 }
663 }
666 lk += len(setValue)
667 for i := 1; i < len(keys); i++ {
668 if string(keys[i][0]) == "[" {
669 // []
670 lk += 2
671 } else {
672 // {"keys[i]":setValue}
673 lk += len(keys[i]) + 5
674 }
675 }
677 if object && !isIndex {
678 // }
679 lk += 1
680 }
682 return lk
685func WriteToBuffer(buffer []byte, str string) int {
686 copy(buffer, str)
687 return len(str)
690/*
692Del - Receives existing data structure, path to delete.
694Returns:
695`data` - return modified data
697*/
698func Delete(data []byte, keys ...string) []byte {
699 lk := len(keys)
700 if lk == 0 {
701 return data[:0]
702 }
704 array := false
705 if len(keys[lk-1]) > 0 && string(keys[lk-1][0]) == "[" {
706 array = true
707 }
709 var startOffset, keyOffset int
710 endOffset := len(data)
711 var err error
712 if !array {
713 if len(keys) > 1 {
714 _, _, startOffset, endOffset, err = internalGet(data, keys[:lk-1]...)
715 if err == KeyPathNotFoundError {
716 // problem parsing the data
717 return data
718 }
719 }
721 keyOffset, err = findKeyStart(data[startOffset:endOffset], keys[lk-1])
722 if err == KeyPathNotFoundError {
723 // problem parsing the data
724 return data
725 }
726 keyOffset += startOffset
727 _, _, _, subEndOffset, _ := internalGet(data[startOffset:endOffset], keys[lk-1])
728 endOffset = startOffset + subEndOffset
729 tokEnd := tokenEnd(data[endOffset:])
730 tokStart := findTokenStart(data[:keyOffset], ","[0])
732 if data[endOffset+tokEnd] == ","[0] {
733 endOffset += tokEnd + 1
734 } else if data[endOffset+tokEnd] == " "[0] && len(data) > endOffset+tokEnd+1 && data[endOffset+tokEnd+1] == ","[0] {
735 endOffset += tokEnd + 2
736 } else if data[endOffset+tokEnd] == "}"[0] && data[tokStart] == ","[0] {
737 keyOffset = tokStart
738 }
739 } else {
740 _, _, keyOffset, endOffset, err = internalGet(data, keys...)
741 if err == KeyPathNotFoundError {
742 // problem parsing the data
743 return data
744 }
746 tokEnd := tokenEnd(data[endOffset:])
747 tokStart := findTokenStart(data[:keyOffset], ","[0])
749 if data[endOffset+tokEnd] == ","[0] {
750 endOffset += tokEnd + 1
751 } else if data[endOffset+tokEnd] == "]"[0] && data[tokStart] == ","[0] {
752 keyOffset = tokStart
753 }
754 }
756 // We need to remove remaining trailing comma if we delete las element in the object
757 prevTok := lastToken(data[:keyOffset])
758 remainedValue := data[endOffset:]
760 var newOffset int
761 if nextToken(remainedValue) > -1 && remainedValue[nextToken(remainedValue)] == '}' && data[prevTok] == ',' {
762 newOffset = prevTok
763 } else {
764 newOffset = prevTok + 1
765 }
767 // We have to make a copy here if we don't want to mangle the original data, because byte slices are
768 // accessed by reference and not by value
769 dataCopy := make([]byte, len(data))
770 copy(dataCopy, data)
771 data = append(dataCopy[:newOffset], dataCopy[endOffset:]...)
773 return data
776/*
778Set - Receives existing data structure, path to set, and data to set at that key.
780Returns:
781`value` - modified byte array
782`err` - On any parsing error
784*/
785func Set(data []byte, setValue []byte, keys ...string) (value []byte, err error) {
786 // ensure keys are set
787 if len(keys) == 0 {
788 return nil, KeyPathNotFoundError
789 }
791 _, _, startOffset, endOffset, err := internalGet(data, keys...)
792 if err != nil {
793 if err != KeyPathNotFoundError {
794 // problem parsing the data
795 return nil, err
796 }
797 // full path doesnt exist
798 // does any subpath exist?
799 var depth int
800 for i := range keys {
801 _, _, start, end, sErr := internalGet(data, keys[:i+1]...)
802 if sErr != nil {
803 break
804 } else {
805 endOffset = end
806 startOffset = start
807 depth++
808 }
809 }
810 comma := true
811 object := false
812 if endOffset == -1 {
813 firstToken := nextToken(data)
814 // We can't set a top-level key if data isn't an object
815 if firstToken < 0 || data[firstToken] != '{' {
816 return nil, KeyPathNotFoundError
817 }
818 // Don't need a comma if the input is an empty object
819 secondToken := firstToken + 1 + nextToken(data[firstToken+1:])
820 if data[secondToken] == '}' {
821 comma = false
822 }
823 // Set the top level key at the end (accounting for any trailing whitespace)
824 // This assumes last token is valid like '}', could check and return error
825 endOffset = lastToken(data)
826 }
827 depthOffset := endOffset
828 if depth != 0 {
829 // if subpath is a non-empty object, add to it
830 // or if subpath is a non-empty array, add to it
831 if (data[startOffset] == '{' && data[startOffset+1+nextToken(data[startOffset+1:])] != '}') ||
832 (data[startOffset] == '[' && data[startOffset+1+nextToken(data[startOffset+1:])] == '{') && keys[depth:][0][0] == 91 {
833 depthOffset--
834 startOffset = depthOffset
835 // otherwise, over-write it with a new object
836 } else {
837 comma = false
838 object = true
839 }
840 } else {
841 startOffset = depthOffset
842 }
843 value = append(data[:startOffset], append(createInsertComponent(keys[depth:], setValue, comma, object), data[depthOffset:]...)...)
844 } else {
845 // path currently exists
846 startComponent := data[:startOffset]
847 endComponent := data[endOffset:]
849 value = make([]byte, len(startComponent)+len(endComponent)+len(setValue))
850 newEndOffset := startOffset + len(setValue)
851 copy(value[0:startOffset], startComponent)
852 copy(value[startOffset:newEndOffset], setValue)
853 copy(value[newEndOffset:], endComponent)
854 }
855 return value, nil
858func getType(data []byte, offset int) ([]byte, ValueType, int, error) {
859 var dataType ValueType
860 endOffset := offset
862 // if string value
863 if data[offset] == '"' {
864 dataType = String
865 if idx, _ := stringEnd(data[offset+1:]); idx != -1 {
866 endOffset += idx + 1
867 } else {
868 return nil, dataType, offset, MalformedStringError
869 }
870 } else if data[offset] == '[' { // if array value
871 dataType = Array
872 // break label, for stopping nested loops
873 endOffset = blockEnd(data[offset:], '[', ']')
875 if endOffset == -1 {
876 return nil, dataType, offset, MalformedArrayError
877 }
879 endOffset += offset
880 } else if data[offset] == '{' { // if object value
881 dataType = Object
882 // break label, for stopping nested loops
883 endOffset = blockEnd(data[offset:], '{', '}')
885 if endOffset == -1 {
886 return nil, dataType, offset, MalformedObjectError
887 }
889 endOffset += offset
890 } else {
891 // Number, Boolean or None
892 end := tokenEnd(data[endOffset:])
894 if end == -1 {
895 return nil, dataType, offset, MalformedValueError
896 }
898 value := data[offset : endOffset+end]
900 switch data[offset] {
901 case 't', 'f': // true or false
902 if bytes.Equal(value, trueLiteral) || bytes.Equal(value, falseLiteral) {
903 dataType = Boolean
904 } else {
905 return nil, Unknown, offset, UnknownValueTypeError
906 }
907 case 'u', 'n': // undefined or null
908 if bytes.Equal(value, nullLiteral) {
909 dataType = Null
910 } else {
911 return nil, Unknown, offset, UnknownValueTypeError
912 }
913 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-':
914 dataType = Number
915 default:
916 return nil, Unknown, offset, UnknownValueTypeError
917 }
919 endOffset += end
920 }
921 return data[offset:endOffset], dataType, endOffset, nil
924/*
925Get - Receives data structure, and key path to extract value from.
927Returns:
928`value` - Pointer to original data structure containing key value, or just empty slice if nothing found or error
929`dataType` - Can be: `NotExist`, `String`, `Number`, `Object`, `Array`, `Boolean` or `Null`
930`offset` - Offset from provided data structure where key value ends. Used mostly internally, for example for `ArrayEach` helper.
931`err` - If key not found or any other parsing issue it should return error. If key not found it also sets `dataType` to `NotExist`
933Accept multiple keys to specify path to JSON value (in case of quering nested structures).
934If no keys provided it will try to extract closest JSON value (simple ones or object/array), useful for reading streams or arrays, see `ArrayEach` implementation.
935*/
936func Get(data []byte, keys ...string) (value []byte, dataType ValueType, offset int, err error) {
937 a, b, _, d, e := internalGet(data, keys...)
938 return a, b, d, e
941func internalGet(data []byte, keys ...string) (value []byte, dataType ValueType, offset, endOffset int, err error) {
942 if len(keys) > 0 {
943 if offset = searchKeys(data, keys...); offset == -1 {
944 return nil, NotExist, -1, -1, KeyPathNotFoundError
945 }
946 }
948 // Go to closest value
949 nO := nextToken(data[offset:])
950 if nO == -1 {
951 return nil, NotExist, offset, -1, MalformedJsonError
952 }
954 offset += nO
955 value, dataType, endOffset, err = getType(data, offset)
956 if err != nil {
957 return value, dataType, offset, endOffset, err
958 }
960 // Strip quotes from string values
961 if dataType == String {
962 value = value[1 : len(value)-1]
963 }
965 return value[:len(value):len(value)], dataType, offset, endOffset, nil
968// ArrayEach is used when iterating arrays, accepts a callback function with the same return arguments as `Get`.
969func ArrayEach(data []byte, cb func(value []byte, dataType ValueType, offset int, err error), keys ...string) (offset int, err error) {
970 if len(data) == 0 {
971 return -1, MalformedObjectError
972 }
974 nT := nextToken(data)
975 if nT == -1 {
976 return -1, MalformedJsonError
977 }
979 offset = nT + 1
981 if len(keys) > 0 {
982 if offset = searchKeys(data, keys...); offset == -1 {
983 return offset, KeyPathNotFoundError
984 }
986 // Go to closest value
987 nO := nextToken(data[offset:])
988 if nO == -1 {
989 return offset, MalformedJsonError
990 }
992 offset += nO
994 if data[offset] != '[' {
995 return offset, MalformedArrayError
996 }
998 offset++
999 }
1001 nO := nextToken(data[offset:])
1002 if nO == -1 {
1003 return offset, MalformedJsonError
1006 offset += nO
1008 if data[offset] == ']' {
1009 return offset, nil
1012 for true {
1013 v, t, o, e := Get(data[offset:])
1015 if e != nil {
1016 return offset, e
1019 if o == 0 {
1020 break
1023 if t != NotExist {
1024 cb(v, t, offset+o-len(v), e)
1027 if e != nil {
1028 break
1031 offset += o
1033 skipToToken := nextToken(data[offset:])
1034 if skipToToken == -1 {
1035 return offset, MalformedArrayError
1037 offset += skipToToken
1039 if data[offset] == ']' {
1040 break
1043 if data[offset] != ',' {
1044 return offset, MalformedArrayError
1047 offset++
1050 return offset, nil
1053// ObjectEach iterates over the key-value pairs of a JSON object, invoking a given callback for each such entry
1054func ObjectEach(data []byte, callback func(key []byte, value []byte, dataType ValueType, offset int) error, keys ...string) (err error) {
1055 offset := 0
1057 // Descend to the desired key, if requested
1058 if len(keys) > 0 {
1059 if off := searchKeys(data, keys...); off == -1 {
1060 return KeyPathNotFoundError
1061 } else {
1062 offset = off
1066 // Validate and skip past opening brace
1067 if off := nextToken(data[offset:]); off == -1 {
1068 return MalformedObjectError
1069 } else if offset += off; data[offset] != '{' {
1070 return MalformedObjectError
1071 } else {
1072 offset++
1075 // Skip to the first token inside the object, or stop if we find the ending brace
1076 if off := nextToken(data[offset:]); off == -1 {
1077 return MalformedJsonError
1078 } else if offset += off; data[offset] == '}' {
1079 return nil
1082 // Loop pre-condition: data[offset] points to what should be either the next entry's key, or the closing brace (if it's anything else, the JSON is malformed)
1083 for offset < len(data) {
1084 // Step 1: find the next key
1085 var key []byte
1087 // Check what the the next token is: start of string, end of object, or something else (error)
1088 switch data[offset] {
1089 case '"':
1090 offset++ // accept as string and skip opening quote
1091 case '}':
1092 return nil // we found the end of the object; stop and return success
1093 default:
1094 return MalformedObjectError
1097 // Find the end of the key string
1098 var keyEscaped bool
1099 if off, esc := stringEnd(data[offset:]); off == -1 {
1100 return MalformedJsonError
1101 } else {
1102 key, keyEscaped = data[offset:offset+off-1], esc
1103 offset += off
1106 // Unescape the string if needed
1107 if keyEscaped {
1108 var stackbuf [unescapeStackBufSize]byte // stack-allocated array for allocation-free unescaping of small strings
1109 if keyUnescaped, err := Unescape(key, stackbuf[:]); err != nil {
1110 return MalformedStringEscapeError
1111 } else {
1112 key = keyUnescaped
1116 // Step 2: skip the colon
1117 if off := nextToken(data[offset:]); off == -1 {
1118 return MalformedJsonError
1119 } else if offset += off; data[offset] != ':' {
1120 return MalformedJsonError
1121 } else {
1122 offset++
1125 // Step 3: find the associated value, then invoke the callback
1126 if value, valueType, off, err := Get(data[offset:]); err != nil {
1127 return err
1128 } else if err := callback(key, value, valueType, offset+off); err != nil { // Invoke the callback here!
1129 return err
1130 } else {
1131 offset += off
1134 // Step 4: skip over the next comma to the following token, or stop if we hit the ending brace
1135 if off := nextToken(data[offset:]); off == -1 {
1136 return MalformedArrayError
1137 } else {
1138 offset += off
1139 switch data[offset] {
1140 case '}':
1141 return nil // Stop if we hit the close brace
1142 case ',':
1143 offset++ // Ignore the comma
1144 default:
1145 return MalformedObjectError
1149 // Skip to the next token after the comma
1150 if off := nextToken(data[offset:]); off == -1 {
1151 return MalformedArrayError
1152 } else {
1153 offset += off
1157 return MalformedObjectError // we shouldn't get here; it's expected that we will return via finding the ending brace
1160// GetUnsafeString returns the value retrieved by `Get`, use creates string without memory allocation by mapping string to slice memory. It does not handle escape symbols.
1161func GetUnsafeString(data []byte, keys ...string) (val string, err error) {
1162 v, _, _, e := Get(data, keys...)
1164 if e != nil {
1165 return "", e
1168 return bytesToString(&v), nil
1171// GetString returns the value retrieved by `Get`, cast to a string if possible, trying to properly handle escape and utf8 symbols
1172// If key data type do not match, it will return an error.
1173func GetString(data []byte, keys ...string) (val string, err error) {
1174 v, t, _, e := Get(data, keys...)
1176 if e != nil {
1177 return "", e
1180 if t != String {
1181 return "", fmt.Errorf("Value is not a string: %s", string(v))
1184 // If no escapes return raw content
1185 if bytes.IndexByte(v, '\\') == -1 {
1186 return string(v), nil
1189 return ParseString(v)
1192// GetFloat returns the value retrieved by `Get`, cast to a float64 if possible.
1193// The offset is the same as in `Get`.
1194// If key data type do not match, it will return an error.
1195func GetFloat(data []byte, keys ...string) (val float64, err error) {
1196 v, t, _, e := Get(data, keys...)
1198 if e != nil {
1199 return 0, e
1202 if t != Number {
1203 return 0, fmt.Errorf("Value is not a number: %s", string(v))
1206 return ParseFloat(v)
1209// GetInt returns the value retrieved by `Get`, cast to a int64 if possible.
1210// If key data type do not match, it will return an error.
1211func GetInt(data []byte, keys ...string) (val int64, err error) {
1212 v, t, _, e := Get(data, keys...)
1214 if e != nil {
1215 return 0, e
1218 if t != Number {
1219 return 0, fmt.Errorf("Value is not a number: %s", string(v))
1222 return ParseInt(v)
1225// GetBoolean returns the value retrieved by `Get`, cast to a bool if possible.
1226// The offset is the same as in `Get`.
1227// If key data type do not match, it will return error.
1228func GetBoolean(data []byte, keys ...string) (val bool, err error) {
1229 v, t, _, e := Get(data, keys...)
1231 if e != nil {
1232 return false, e
1235 if t != Boolean {
1236 return false, fmt.Errorf("Value is not a boolean: %s", string(v))
1239 return ParseBoolean(v)
1242// ParseBoolean parses a Boolean ValueType into a Go bool (not particularly useful, but here for completeness)
1243func ParseBoolean(b []byte) (bool, error) {
1244 switch {
1245 case bytes.Equal(b, trueLiteral):
1246 return true, nil
1247 case bytes.Equal(b, falseLiteral):
1248 return false, nil
1249 default:
1250 return false, MalformedValueError
1254// ParseString parses a String ValueType into a Go string (the main parsing work is unescaping the JSON string)
1255func ParseString(b []byte) (string, error) {
1256 var stackbuf [unescapeStackBufSize]byte // stack-allocated array for allocation-free unescaping of small strings
1257 if bU, err := Unescape(b, stackbuf[:]); err != nil {
1258 return "", MalformedValueError
1259 } else {
1260 return string(bU), nil
1264// ParseNumber parses a Number ValueType into a Go float64
1265func ParseFloat(b []byte) (float64, error) {
1266 if v, err := parseFloat(&b); err != nil {
1267 return 0, MalformedValueError
1268 } else {
1269 return v, nil
1273// ParseInt parses a Number ValueType into a Go int64
1274func ParseInt(b []byte) (int64, error) {
1275 if v, ok, overflow := parseInt(b); !ok {
1276 if overflow {
1277 return 0, OverflowIntegerError
1279 return 0, MalformedValueError
1280 } else {
1281 return v, nil