mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-26 20:30:10 +01:00
cd87ca303f
Previously certain errors in timestamps and/or values could be silently skipped, which could lead to samples with zero values stored in the database. Updates https://github.com/VictoriaMetrics/vmctl/issues/25
977 lines
22 KiB
Go
977 lines
22 KiB
Go
package fastjson
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/valyala/fastjson/fastfloat"
|
|
"strconv"
|
|
"strings"
|
|
"unicode/utf16"
|
|
)
|
|
|
|
// Parser parses JSON.
|
|
//
|
|
// Parser may be re-used for subsequent parsing.
|
|
//
|
|
// Parser cannot be used from concurrent goroutines.
|
|
// Use per-goroutine parsers or ParserPool instead.
|
|
type Parser struct {
|
|
// b contains working copy of the string to be parsed.
|
|
b []byte
|
|
|
|
// c is a cache for json values.
|
|
c cache
|
|
}
|
|
|
|
// Parse parses s containing JSON.
|
|
//
|
|
// The returned value is valid until the next call to Parse*.
|
|
//
|
|
// Use Scanner if a stream of JSON values must be parsed.
|
|
func (p *Parser) Parse(s string) (*Value, error) {
|
|
s = skipWS(s)
|
|
p.b = append(p.b[:0], s...)
|
|
p.c.reset()
|
|
|
|
v, tail, err := parseValue(b2s(p.b), &p.c, 0)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot parse JSON: %s; unparsed tail: %q", err, startEndString(tail))
|
|
}
|
|
tail = skipWS(tail)
|
|
if len(tail) > 0 {
|
|
return nil, fmt.Errorf("unexpected tail: %q", startEndString(tail))
|
|
}
|
|
return v, nil
|
|
}
|
|
|
|
// ParseBytes parses b containing JSON.
|
|
//
|
|
// The returned Value is valid until the next call to Parse*.
|
|
//
|
|
// Use Scanner if a stream of JSON values must be parsed.
|
|
func (p *Parser) ParseBytes(b []byte) (*Value, error) {
|
|
return p.Parse(b2s(b))
|
|
}
|
|
|
|
type cache struct {
|
|
vs []Value
|
|
}
|
|
|
|
func (c *cache) reset() {
|
|
c.vs = c.vs[:0]
|
|
}
|
|
|
|
func (c *cache) getValue() *Value {
|
|
if cap(c.vs) > len(c.vs) {
|
|
c.vs = c.vs[:len(c.vs)+1]
|
|
} else {
|
|
c.vs = append(c.vs, Value{})
|
|
}
|
|
// Do not reset the value, since the caller must properly init it.
|
|
return &c.vs[len(c.vs)-1]
|
|
}
|
|
|
|
func skipWS(s string) string {
|
|
if len(s) == 0 || s[0] > 0x20 {
|
|
// Fast path.
|
|
return s
|
|
}
|
|
return skipWSSlow(s)
|
|
}
|
|
|
|
func skipWSSlow(s string) string {
|
|
if len(s) == 0 || s[0] != 0x20 && s[0] != 0x0A && s[0] != 0x09 && s[0] != 0x0D {
|
|
return s
|
|
}
|
|
for i := 1; i < len(s); i++ {
|
|
if s[i] != 0x20 && s[i] != 0x0A && s[i] != 0x09 && s[i] != 0x0D {
|
|
return s[i:]
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
type kv struct {
|
|
k string
|
|
v *Value
|
|
}
|
|
|
|
// MaxDepth is the maximum depth for nested JSON.
|
|
const MaxDepth = 300
|
|
|
|
func parseValue(s string, c *cache, depth int) (*Value, string, error) {
|
|
if len(s) == 0 {
|
|
return nil, s, fmt.Errorf("cannot parse empty string")
|
|
}
|
|
depth++
|
|
if depth > MaxDepth {
|
|
return nil, s, fmt.Errorf("too big depth for the nested JSON; it exceeds %d", MaxDepth)
|
|
}
|
|
|
|
if s[0] == '{' {
|
|
v, tail, err := parseObject(s[1:], c, depth)
|
|
if err != nil {
|
|
return nil, tail, fmt.Errorf("cannot parse object: %s", err)
|
|
}
|
|
return v, tail, nil
|
|
}
|
|
if s[0] == '[' {
|
|
v, tail, err := parseArray(s[1:], c, depth)
|
|
if err != nil {
|
|
return nil, tail, fmt.Errorf("cannot parse array: %s", err)
|
|
}
|
|
return v, tail, nil
|
|
}
|
|
if s[0] == '"' {
|
|
ss, tail, err := parseRawString(s[1:])
|
|
if err != nil {
|
|
return nil, tail, fmt.Errorf("cannot parse string: %s", err)
|
|
}
|
|
v := c.getValue()
|
|
v.t = typeRawString
|
|
v.s = ss
|
|
return v, tail, nil
|
|
}
|
|
if s[0] == 't' {
|
|
if len(s) < len("true") || s[:len("true")] != "true" {
|
|
return nil, s, fmt.Errorf("unexpected value found: %q", s)
|
|
}
|
|
return valueTrue, s[len("true"):], nil
|
|
}
|
|
if s[0] == 'f' {
|
|
if len(s) < len("false") || s[:len("false")] != "false" {
|
|
return nil, s, fmt.Errorf("unexpected value found: %q", s)
|
|
}
|
|
return valueFalse, s[len("false"):], nil
|
|
}
|
|
if s[0] == 'n' {
|
|
if len(s) < len("null") || s[:len("null")] != "null" {
|
|
// Try parsing NaN
|
|
if len(s) >= 3 && strings.EqualFold(s[:3], "nan") {
|
|
v := c.getValue()
|
|
v.t = TypeNumber
|
|
v.s = s[:3]
|
|
return v, s[3:], nil
|
|
}
|
|
return nil, s, fmt.Errorf("unexpected value found: %q", s)
|
|
}
|
|
return valueNull, s[len("null"):], nil
|
|
}
|
|
|
|
ns, tail, err := parseRawNumber(s)
|
|
if err != nil {
|
|
return nil, tail, fmt.Errorf("cannot parse number: %s", err)
|
|
}
|
|
v := c.getValue()
|
|
v.t = TypeNumber
|
|
v.s = ns
|
|
return v, tail, nil
|
|
}
|
|
|
|
func parseArray(s string, c *cache, depth int) (*Value, string, error) {
|
|
s = skipWS(s)
|
|
if len(s) == 0 {
|
|
return nil, s, fmt.Errorf("missing ']'")
|
|
}
|
|
|
|
if s[0] == ']' {
|
|
v := c.getValue()
|
|
v.t = TypeArray
|
|
v.a = v.a[:0]
|
|
return v, s[1:], nil
|
|
}
|
|
|
|
a := c.getValue()
|
|
a.t = TypeArray
|
|
a.a = a.a[:0]
|
|
for {
|
|
var v *Value
|
|
var err error
|
|
|
|
s = skipWS(s)
|
|
v, s, err = parseValue(s, c, depth)
|
|
if err != nil {
|
|
return nil, s, fmt.Errorf("cannot parse array value: %s", err)
|
|
}
|
|
a.a = append(a.a, v)
|
|
|
|
s = skipWS(s)
|
|
if len(s) == 0 {
|
|
return nil, s, fmt.Errorf("unexpected end of array")
|
|
}
|
|
if s[0] == ',' {
|
|
s = s[1:]
|
|
continue
|
|
}
|
|
if s[0] == ']' {
|
|
s = s[1:]
|
|
return a, s, nil
|
|
}
|
|
return nil, s, fmt.Errorf("missing ',' after array value")
|
|
}
|
|
}
|
|
|
|
func parseObject(s string, c *cache, depth int) (*Value, string, error) {
|
|
s = skipWS(s)
|
|
if len(s) == 0 {
|
|
return nil, s, fmt.Errorf("missing '}'")
|
|
}
|
|
|
|
if s[0] == '}' {
|
|
v := c.getValue()
|
|
v.t = TypeObject
|
|
v.o.reset()
|
|
return v, s[1:], nil
|
|
}
|
|
|
|
o := c.getValue()
|
|
o.t = TypeObject
|
|
o.o.reset()
|
|
for {
|
|
var err error
|
|
kv := o.o.getKV()
|
|
|
|
// Parse key.
|
|
s = skipWS(s)
|
|
if len(s) == 0 || s[0] != '"' {
|
|
return nil, s, fmt.Errorf(`cannot find opening '"" for object key`)
|
|
}
|
|
kv.k, s, err = parseRawKey(s[1:])
|
|
if err != nil {
|
|
return nil, s, fmt.Errorf("cannot parse object key: %s", err)
|
|
}
|
|
s = skipWS(s)
|
|
if len(s) == 0 || s[0] != ':' {
|
|
return nil, s, fmt.Errorf("missing ':' after object key")
|
|
}
|
|
s = s[1:]
|
|
|
|
// Parse value
|
|
s = skipWS(s)
|
|
kv.v, s, err = parseValue(s, c, depth)
|
|
if err != nil {
|
|
return nil, s, fmt.Errorf("cannot parse object value: %s", err)
|
|
}
|
|
s = skipWS(s)
|
|
if len(s) == 0 {
|
|
return nil, s, fmt.Errorf("unexpected end of object")
|
|
}
|
|
if s[0] == ',' {
|
|
s = s[1:]
|
|
continue
|
|
}
|
|
if s[0] == '}' {
|
|
return o, s[1:], nil
|
|
}
|
|
return nil, s, fmt.Errorf("missing ',' after object value")
|
|
}
|
|
}
|
|
|
|
func escapeString(dst []byte, s string) []byte {
|
|
if !hasSpecialChars(s) {
|
|
// Fast path - nothing to escape.
|
|
dst = append(dst, '"')
|
|
dst = append(dst, s...)
|
|
dst = append(dst, '"')
|
|
return dst
|
|
}
|
|
|
|
// Slow path.
|
|
return strconv.AppendQuote(dst, s)
|
|
}
|
|
|
|
func hasSpecialChars(s string) bool {
|
|
if strings.IndexByte(s, '"') >= 0 || strings.IndexByte(s, '\\') >= 0 {
|
|
return true
|
|
}
|
|
for i := 0; i < len(s); i++ {
|
|
if s[i] < 0x20 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func unescapeStringBestEffort(s string) string {
|
|
n := strings.IndexByte(s, '\\')
|
|
if n < 0 {
|
|
// Fast path - nothing to unescape.
|
|
return s
|
|
}
|
|
|
|
// Slow path - unescape string.
|
|
b := s2b(s) // It is safe to do, since s points to a byte slice in Parser.b.
|
|
b = b[:n]
|
|
s = s[n+1:]
|
|
for len(s) > 0 {
|
|
ch := s[0]
|
|
s = s[1:]
|
|
switch ch {
|
|
case '"':
|
|
b = append(b, '"')
|
|
case '\\':
|
|
b = append(b, '\\')
|
|
case '/':
|
|
b = append(b, '/')
|
|
case 'b':
|
|
b = append(b, '\b')
|
|
case 'f':
|
|
b = append(b, '\f')
|
|
case 'n':
|
|
b = append(b, '\n')
|
|
case 'r':
|
|
b = append(b, '\r')
|
|
case 't':
|
|
b = append(b, '\t')
|
|
case 'u':
|
|
if len(s) < 4 {
|
|
// Too short escape sequence. Just store it unchanged.
|
|
b = append(b, "\\u"...)
|
|
break
|
|
}
|
|
xs := s[:4]
|
|
x, err := strconv.ParseUint(xs, 16, 16)
|
|
if err != nil {
|
|
// Invalid escape sequence. Just store it unchanged.
|
|
b = append(b, "\\u"...)
|
|
break
|
|
}
|
|
s = s[4:]
|
|
if !utf16.IsSurrogate(rune(x)) {
|
|
b = append(b, string(rune(x))...)
|
|
break
|
|
}
|
|
|
|
// Surrogate.
|
|
// See https://en.wikipedia.org/wiki/Universal_Character_Set_characters#Surrogates
|
|
if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
|
|
b = append(b, "\\u"...)
|
|
b = append(b, xs...)
|
|
break
|
|
}
|
|
x1, err := strconv.ParseUint(s[2:6], 16, 16)
|
|
if err != nil {
|
|
b = append(b, "\\u"...)
|
|
b = append(b, xs...)
|
|
break
|
|
}
|
|
r := utf16.DecodeRune(rune(x), rune(x1))
|
|
b = append(b, string(r)...)
|
|
s = s[6:]
|
|
default:
|
|
// Unknown escape sequence. Just store it unchanged.
|
|
b = append(b, '\\', ch)
|
|
}
|
|
n = strings.IndexByte(s, '\\')
|
|
if n < 0 {
|
|
b = append(b, s...)
|
|
break
|
|
}
|
|
b = append(b, s[:n]...)
|
|
s = s[n+1:]
|
|
}
|
|
return b2s(b)
|
|
}
|
|
|
|
// parseRawKey is similar to parseRawString, but is optimized
|
|
// for small-sized keys without escape sequences.
|
|
func parseRawKey(s string) (string, string, error) {
|
|
for i := 0; i < len(s); i++ {
|
|
if s[i] == '"' {
|
|
// Fast path.
|
|
return s[:i], s[i+1:], nil
|
|
}
|
|
if s[i] == '\\' {
|
|
// Slow path.
|
|
return parseRawString(s)
|
|
}
|
|
}
|
|
return s, "", fmt.Errorf(`missing closing '"'`)
|
|
}
|
|
|
|
func parseRawString(s string) (string, string, error) {
|
|
n := strings.IndexByte(s, '"')
|
|
if n < 0 {
|
|
return s, "", fmt.Errorf(`missing closing '"'`)
|
|
}
|
|
if n == 0 || s[n-1] != '\\' {
|
|
// Fast path. No escaped ".
|
|
return s[:n], s[n+1:], nil
|
|
}
|
|
|
|
// Slow path - possible escaped " found.
|
|
ss := s
|
|
for {
|
|
i := n - 1
|
|
for i > 0 && s[i-1] == '\\' {
|
|
i--
|
|
}
|
|
if uint(n-i)%2 == 0 {
|
|
return ss[:len(ss)-len(s)+n], s[n+1:], nil
|
|
}
|
|
s = s[n+1:]
|
|
|
|
n = strings.IndexByte(s, '"')
|
|
if n < 0 {
|
|
return ss, "", fmt.Errorf(`missing closing '"'`)
|
|
}
|
|
if n == 0 || s[n-1] != '\\' {
|
|
return ss[:len(ss)-len(s)+n], s[n+1:], nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func parseRawNumber(s string) (string, string, error) {
|
|
// The caller must ensure len(s) > 0
|
|
|
|
// Find the end of the number.
|
|
for i := 0; i < len(s); i++ {
|
|
ch := s[i]
|
|
if (ch >= '0' && ch <= '9') || ch == '.' || ch == '-' || ch == 'e' || ch == 'E' || ch == '+' {
|
|
continue
|
|
}
|
|
if i == 0 || i == 1 && (s[0] == '-' || s[0] == '+') {
|
|
if len(s[i:]) >= 3 {
|
|
xs := s[i : i+3]
|
|
if strings.EqualFold(xs, "inf") || strings.EqualFold(xs, "nan") {
|
|
return s[:i+3], s[i+3:], nil
|
|
}
|
|
}
|
|
return "", s, fmt.Errorf("unexpected char: %q", s[:1])
|
|
}
|
|
ns := s[:i]
|
|
s = s[i:]
|
|
return ns, s, nil
|
|
}
|
|
return s, "", nil
|
|
}
|
|
|
|
// Object represents JSON object.
|
|
//
|
|
// Object cannot be used from concurrent goroutines.
|
|
// Use per-goroutine parsers or ParserPool instead.
|
|
type Object struct {
|
|
kvs []kv
|
|
keysUnescaped bool
|
|
}
|
|
|
|
func (o *Object) reset() {
|
|
o.kvs = o.kvs[:0]
|
|
o.keysUnescaped = false
|
|
}
|
|
|
|
// MarshalTo appends marshaled o to dst and returns the result.
|
|
func (o *Object) MarshalTo(dst []byte) []byte {
|
|
dst = append(dst, '{')
|
|
for i, kv := range o.kvs {
|
|
if o.keysUnescaped {
|
|
dst = escapeString(dst, kv.k)
|
|
} else {
|
|
dst = append(dst, '"')
|
|
dst = append(dst, kv.k...)
|
|
dst = append(dst, '"')
|
|
}
|
|
dst = append(dst, ':')
|
|
dst = kv.v.MarshalTo(dst)
|
|
if i != len(o.kvs)-1 {
|
|
dst = append(dst, ',')
|
|
}
|
|
}
|
|
dst = append(dst, '}')
|
|
return dst
|
|
}
|
|
|
|
// String returns string representation for the o.
|
|
//
|
|
// This function is for debugging purposes only. It isn't optimized for speed.
|
|
// See MarshalTo instead.
|
|
func (o *Object) String() string {
|
|
b := o.MarshalTo(nil)
|
|
// It is safe converting b to string without allocation, since b is no longer
|
|
// reachable after this line.
|
|
return b2s(b)
|
|
}
|
|
|
|
func (o *Object) getKV() *kv {
|
|
if cap(o.kvs) > len(o.kvs) {
|
|
o.kvs = o.kvs[:len(o.kvs)+1]
|
|
} else {
|
|
o.kvs = append(o.kvs, kv{})
|
|
}
|
|
return &o.kvs[len(o.kvs)-1]
|
|
}
|
|
|
|
func (o *Object) unescapeKeys() {
|
|
if o.keysUnescaped {
|
|
return
|
|
}
|
|
kvs := o.kvs
|
|
for i := range kvs {
|
|
kv := &kvs[i]
|
|
kv.k = unescapeStringBestEffort(kv.k)
|
|
}
|
|
o.keysUnescaped = true
|
|
}
|
|
|
|
// Len returns the number of items in the o.
|
|
func (o *Object) Len() int {
|
|
return len(o.kvs)
|
|
}
|
|
|
|
// Get returns the value for the given key in the o.
|
|
//
|
|
// Returns nil if the value for the given key isn't found.
|
|
//
|
|
// The returned value is valid until Parse is called on the Parser returned o.
|
|
func (o *Object) Get(key string) *Value {
|
|
if !o.keysUnescaped && strings.IndexByte(key, '\\') < 0 {
|
|
// Fast path - try searching for the key without object keys unescaping.
|
|
for _, kv := range o.kvs {
|
|
if kv.k == key {
|
|
return kv.v
|
|
}
|
|
}
|
|
}
|
|
|
|
// Slow path - unescape object keys.
|
|
o.unescapeKeys()
|
|
|
|
for _, kv := range o.kvs {
|
|
if kv.k == key {
|
|
return kv.v
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Visit calls f for each item in the o in the original order
|
|
// of the parsed JSON.
|
|
//
|
|
// f cannot hold key and/or v after returning.
|
|
func (o *Object) Visit(f func(key []byte, v *Value)) {
|
|
if o == nil {
|
|
return
|
|
}
|
|
|
|
o.unescapeKeys()
|
|
|
|
for _, kv := range o.kvs {
|
|
f(s2b(kv.k), kv.v)
|
|
}
|
|
}
|
|
|
|
// Value represents any JSON value.
|
|
//
|
|
// Call Type in order to determine the actual type of the JSON value.
|
|
//
|
|
// Value cannot be used from concurrent goroutines.
|
|
// Use per-goroutine parsers or ParserPool instead.
|
|
type Value struct {
|
|
o Object
|
|
a []*Value
|
|
s string
|
|
t Type
|
|
}
|
|
|
|
// MarshalTo appends marshaled v to dst and returns the result.
|
|
func (v *Value) MarshalTo(dst []byte) []byte {
|
|
switch v.t {
|
|
case typeRawString:
|
|
dst = append(dst, '"')
|
|
dst = append(dst, v.s...)
|
|
dst = append(dst, '"')
|
|
return dst
|
|
case TypeObject:
|
|
return v.o.MarshalTo(dst)
|
|
case TypeArray:
|
|
dst = append(dst, '[')
|
|
for i, vv := range v.a {
|
|
dst = vv.MarshalTo(dst)
|
|
if i != len(v.a)-1 {
|
|
dst = append(dst, ',')
|
|
}
|
|
}
|
|
dst = append(dst, ']')
|
|
return dst
|
|
case TypeString:
|
|
return escapeString(dst, v.s)
|
|
case TypeNumber:
|
|
return append(dst, v.s...)
|
|
case TypeTrue:
|
|
return append(dst, "true"...)
|
|
case TypeFalse:
|
|
return append(dst, "false"...)
|
|
case TypeNull:
|
|
return append(dst, "null"...)
|
|
default:
|
|
panic(fmt.Errorf("BUG: unexpected Value type: %d", v.t))
|
|
}
|
|
}
|
|
|
|
// String returns string representation of the v.
|
|
//
|
|
// The function is for debugging purposes only. It isn't optimized for speed.
|
|
// See MarshalTo instead.
|
|
//
|
|
// Don't confuse this function with StringBytes, which must be called
|
|
// for obtaining the underlying JSON string for the v.
|
|
func (v *Value) String() string {
|
|
b := v.MarshalTo(nil)
|
|
// It is safe converting b to string without allocation, since b is no longer
|
|
// reachable after this line.
|
|
return b2s(b)
|
|
}
|
|
|
|
// Type represents JSON type.
|
|
type Type int
|
|
|
|
const (
|
|
// TypeNull is JSON null.
|
|
TypeNull Type = 0
|
|
|
|
// TypeObject is JSON object type.
|
|
TypeObject Type = 1
|
|
|
|
// TypeArray is JSON array type.
|
|
TypeArray Type = 2
|
|
|
|
// TypeString is JSON string type.
|
|
TypeString Type = 3
|
|
|
|
// TypeNumber is JSON number type.
|
|
TypeNumber Type = 4
|
|
|
|
// TypeTrue is JSON true.
|
|
TypeTrue Type = 5
|
|
|
|
// TypeFalse is JSON false.
|
|
TypeFalse Type = 6
|
|
|
|
typeRawString Type = 7
|
|
)
|
|
|
|
// String returns string representation of t.
|
|
func (t Type) String() string {
|
|
switch t {
|
|
case TypeObject:
|
|
return "object"
|
|
case TypeArray:
|
|
return "array"
|
|
case TypeString:
|
|
return "string"
|
|
case TypeNumber:
|
|
return "number"
|
|
case TypeTrue:
|
|
return "true"
|
|
case TypeFalse:
|
|
return "false"
|
|
case TypeNull:
|
|
return "null"
|
|
|
|
// typeRawString is skipped intentionally,
|
|
// since it shouldn't be visible to user.
|
|
default:
|
|
panic(fmt.Errorf("BUG: unknown Value type: %d", t))
|
|
}
|
|
}
|
|
|
|
// Type returns the type of the v.
|
|
func (v *Value) Type() Type {
|
|
if v.t == typeRawString {
|
|
v.s = unescapeStringBestEffort(v.s)
|
|
v.t = TypeString
|
|
}
|
|
return v.t
|
|
}
|
|
|
|
// Exists returns true if the field exists for the given keys path.
|
|
//
|
|
// Array indexes may be represented as decimal numbers in keys.
|
|
func (v *Value) Exists(keys ...string) bool {
|
|
v = v.Get(keys...)
|
|
return v != nil
|
|
}
|
|
|
|
// Get returns value by the given keys path.
|
|
//
|
|
// Array indexes may be represented as decimal numbers in keys.
|
|
//
|
|
// nil is returned for non-existing keys path.
|
|
//
|
|
// The returned value is valid until Parse is called on the Parser returned v.
|
|
func (v *Value) Get(keys ...string) *Value {
|
|
if v == nil {
|
|
return nil
|
|
}
|
|
for _, key := range keys {
|
|
if v.t == TypeObject {
|
|
v = v.o.Get(key)
|
|
if v == nil {
|
|
return nil
|
|
}
|
|
} else if v.t == TypeArray {
|
|
n, err := strconv.Atoi(key)
|
|
if err != nil || n < 0 || n >= len(v.a) {
|
|
return nil
|
|
}
|
|
v = v.a[n]
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
return v
|
|
}
|
|
|
|
// GetObject returns object value by the given keys path.
|
|
//
|
|
// Array indexes may be represented as decimal numbers in keys.
|
|
//
|
|
// nil is returned for non-existing keys path or for invalid value type.
|
|
//
|
|
// The returned object is valid until Parse is called on the Parser returned v.
|
|
func (v *Value) GetObject(keys ...string) *Object {
|
|
v = v.Get(keys...)
|
|
if v == nil || v.t != TypeObject {
|
|
return nil
|
|
}
|
|
return &v.o
|
|
}
|
|
|
|
// GetArray returns array value by the given keys path.
|
|
//
|
|
// Array indexes may be represented as decimal numbers in keys.
|
|
//
|
|
// nil is returned for non-existing keys path or for invalid value type.
|
|
//
|
|
// The returned array is valid until Parse is called on the Parser returned v.
|
|
func (v *Value) GetArray(keys ...string) []*Value {
|
|
v = v.Get(keys...)
|
|
if v == nil || v.t != TypeArray {
|
|
return nil
|
|
}
|
|
return v.a
|
|
}
|
|
|
|
// GetFloat64 returns float64 value by the given keys path.
|
|
//
|
|
// Array indexes may be represented as decimal numbers in keys.
|
|
//
|
|
// 0 is returned for non-existing keys path or for invalid value type.
|
|
func (v *Value) GetFloat64(keys ...string) float64 {
|
|
v = v.Get(keys...)
|
|
if v == nil || v.Type() != TypeNumber {
|
|
return 0
|
|
}
|
|
return fastfloat.ParseBestEffort(v.s)
|
|
}
|
|
|
|
// GetInt returns int value by the given keys path.
|
|
//
|
|
// Array indexes may be represented as decimal numbers in keys.
|
|
//
|
|
// 0 is returned for non-existing keys path or for invalid value type.
|
|
func (v *Value) GetInt(keys ...string) int {
|
|
v = v.Get(keys...)
|
|
if v == nil || v.Type() != TypeNumber {
|
|
return 0
|
|
}
|
|
n := fastfloat.ParseInt64BestEffort(v.s)
|
|
nn := int(n)
|
|
if int64(nn) != n {
|
|
return 0
|
|
}
|
|
return nn
|
|
}
|
|
|
|
// GetUint returns uint value by the given keys path.
|
|
//
|
|
// Array indexes may be represented as decimal numbers in keys.
|
|
//
|
|
// 0 is returned for non-existing keys path or for invalid value type.
|
|
func (v *Value) GetUint(keys ...string) uint {
|
|
v = v.Get(keys...)
|
|
if v == nil || v.Type() != TypeNumber {
|
|
return 0
|
|
}
|
|
n := fastfloat.ParseUint64BestEffort(v.s)
|
|
nn := uint(n)
|
|
if uint64(nn) != n {
|
|
return 0
|
|
}
|
|
return nn
|
|
}
|
|
|
|
// GetInt64 returns int64 value by the given keys path.
|
|
//
|
|
// Array indexes may be represented as decimal numbers in keys.
|
|
//
|
|
// 0 is returned for non-existing keys path or for invalid value type.
|
|
func (v *Value) GetInt64(keys ...string) int64 {
|
|
v = v.Get(keys...)
|
|
if v == nil || v.Type() != TypeNumber {
|
|
return 0
|
|
}
|
|
return fastfloat.ParseInt64BestEffort(v.s)
|
|
}
|
|
|
|
// GetUint64 returns uint64 value by the given keys path.
|
|
//
|
|
// Array indexes may be represented as decimal numbers in keys.
|
|
//
|
|
// 0 is returned for non-existing keys path or for invalid value type.
|
|
func (v *Value) GetUint64(keys ...string) uint64 {
|
|
v = v.Get(keys...)
|
|
if v == nil || v.Type() != TypeNumber {
|
|
return 0
|
|
}
|
|
return fastfloat.ParseUint64BestEffort(v.s)
|
|
}
|
|
|
|
// GetStringBytes returns string value by the given keys path.
|
|
//
|
|
// Array indexes may be represented as decimal numbers in keys.
|
|
//
|
|
// nil is returned for non-existing keys path or for invalid value type.
|
|
//
|
|
// The returned string is valid until Parse is called on the Parser returned v.
|
|
func (v *Value) GetStringBytes(keys ...string) []byte {
|
|
v = v.Get(keys...)
|
|
if v == nil || v.Type() != TypeString {
|
|
return nil
|
|
}
|
|
return s2b(v.s)
|
|
}
|
|
|
|
// GetBool returns bool value by the given keys path.
|
|
//
|
|
// Array indexes may be represented as decimal numbers in keys.
|
|
//
|
|
// false is returned for non-existing keys path or for invalid value type.
|
|
func (v *Value) GetBool(keys ...string) bool {
|
|
v = v.Get(keys...)
|
|
if v != nil && v.t == TypeTrue {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Object returns the underlying JSON object for the v.
|
|
//
|
|
// The returned object is valid until Parse is called on the Parser returned v.
|
|
//
|
|
// Use GetObject if you don't need error handling.
|
|
func (v *Value) Object() (*Object, error) {
|
|
if v.t != TypeObject {
|
|
return nil, fmt.Errorf("value doesn't contain object; it contains %s", v.Type())
|
|
}
|
|
return &v.o, nil
|
|
}
|
|
|
|
// Array returns the underlying JSON array for the v.
|
|
//
|
|
// The returned array is valid until Parse is called on the Parser returned v.
|
|
//
|
|
// Use GetArray if you don't need error handling.
|
|
func (v *Value) Array() ([]*Value, error) {
|
|
if v.t != TypeArray {
|
|
return nil, fmt.Errorf("value doesn't contain array; it contains %s", v.Type())
|
|
}
|
|
return v.a, nil
|
|
}
|
|
|
|
// StringBytes returns the underlying JSON string for the v.
|
|
//
|
|
// The returned string is valid until Parse is called on the Parser returned v.
|
|
//
|
|
// Use GetStringBytes if you don't need error handling.
|
|
func (v *Value) StringBytes() ([]byte, error) {
|
|
if v.Type() != TypeString {
|
|
return nil, fmt.Errorf("value doesn't contain string; it contains %s", v.Type())
|
|
}
|
|
return s2b(v.s), nil
|
|
}
|
|
|
|
// Float64 returns the underlying JSON number for the v.
|
|
//
|
|
// Use GetFloat64 if you don't need error handling.
|
|
func (v *Value) Float64() (float64, error) {
|
|
if v.Type() != TypeNumber {
|
|
return 0, fmt.Errorf("value doesn't contain number; it contains %s", v.Type())
|
|
}
|
|
return fastfloat.Parse(v.s)
|
|
}
|
|
|
|
// Int returns the underlying JSON int for the v.
|
|
//
|
|
// Use GetInt if you don't need error handling.
|
|
func (v *Value) Int() (int, error) {
|
|
if v.Type() != TypeNumber {
|
|
return 0, fmt.Errorf("value doesn't contain number; it contains %s", v.Type())
|
|
}
|
|
n, err := fastfloat.ParseInt64(v.s)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
nn := int(n)
|
|
if int64(nn) != n {
|
|
return 0, fmt.Errorf("number %q doesn't fit int", v.s)
|
|
}
|
|
return nn, nil
|
|
}
|
|
|
|
// Uint returns the underlying JSON uint for the v.
|
|
//
|
|
// Use GetInt if you don't need error handling.
|
|
func (v *Value) Uint() (uint, error) {
|
|
if v.Type() != TypeNumber {
|
|
return 0, fmt.Errorf("value doesn't contain number; it contains %s", v.Type())
|
|
}
|
|
n, err := fastfloat.ParseUint64(v.s)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
nn := uint(n)
|
|
if uint64(nn) != n {
|
|
return 0, fmt.Errorf("number %q doesn't fit uint", v.s)
|
|
}
|
|
return nn, nil
|
|
}
|
|
|
|
// Int64 returns the underlying JSON int64 for the v.
|
|
//
|
|
// Use GetInt64 if you don't need error handling.
|
|
func (v *Value) Int64() (int64, error) {
|
|
if v.Type() != TypeNumber {
|
|
return 0, fmt.Errorf("value doesn't contain number; it contains %s", v.Type())
|
|
}
|
|
return fastfloat.ParseInt64(v.s)
|
|
}
|
|
|
|
// Uint64 returns the underlying JSON uint64 for the v.
|
|
//
|
|
// Use GetInt64 if you don't need error handling.
|
|
func (v *Value) Uint64() (uint64, error) {
|
|
if v.Type() != TypeNumber {
|
|
return 0, fmt.Errorf("value doesn't contain number; it contains %s", v.Type())
|
|
}
|
|
return fastfloat.ParseUint64(v.s)
|
|
}
|
|
|
|
// Bool returns the underlying JSON bool for the v.
|
|
//
|
|
// Use GetBool if you don't need error handling.
|
|
func (v *Value) Bool() (bool, error) {
|
|
if v.t == TypeTrue {
|
|
return true, nil
|
|
}
|
|
if v.t == TypeFalse {
|
|
return false, nil
|
|
}
|
|
return false, fmt.Errorf("value doesn't contain bool; it contains %s", v.Type())
|
|
}
|
|
|
|
var (
|
|
valueTrue = &Value{t: TypeTrue}
|
|
valueFalse = &Value{t: TypeFalse}
|
|
valueNull = &Value{t: TypeNull}
|
|
)
|