package fastjsonimport ()// 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.typeParserstruct {// b contains working copy of the string to be parsed.b []byte// c is a cache for json values.ccache}// 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 ( *Parser) ( string) (*Value, error) { = skipWS() .b = append(.b[:0], ...) .c.reset() , , := parseValue(b2s(.b), &.c, 0)if != nil {returnnil, fmt.Errorf("cannot parse JSON: %s; unparsed tail: %q", , startEndString()) } = skipWS()iflen() > 0 {returnnil, fmt.Errorf("unexpected tail: %q", startEndString()) }return , 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 ( *Parser) ( []byte) (*Value, error) {return .Parse(b2s())}typecachestruct {vs []Value}func ( *cache) () { .vs = .vs[:0]}func ( *cache) () *Value {ifcap(.vs) > len(.vs) { .vs = .vs[:len(.vs)+1] } else { .vs = append(.vs, Value{}) }// Do not reset the value, since the caller must properly init it.return &.vs[len(.vs)-1]}func ( string) string {iflen() == 0 || [0] > 0x20 {// Fast path.return }returnskipWSSlow()}func ( string) string {iflen() == 0 || [0] != 0x20 && [0] != 0x0A && [0] != 0x09 && [0] != 0x0D {return }for := 1; < len(); ++ {if [] != 0x20 && [] != 0x0A && [] != 0x09 && [] != 0x0D {return [:] } }return""}typekvstruct {kstringv *Value}// MaxDepth is the maximum depth for nested JSON.constMaxDepth = 300func ( string, *cache, int) (*Value, string, error) {iflen() == 0 {returnnil, , fmt.Errorf("cannot parse empty string") } ++if > MaxDepth {returnnil, , fmt.Errorf("too big depth for the nested JSON; it exceeds %d", MaxDepth) }if [0] == '{' { , , := parseObject([1:], , )if != nil {returnnil, , fmt.Errorf("cannot parse object: %s", ) }return , , nil }if [0] == '[' { , , := parseArray([1:], , )if != nil {returnnil, , fmt.Errorf("cannot parse array: %s", ) }return , , nil }if [0] == '"' { , , := parseRawString([1:])if != nil {returnnil, , fmt.Errorf("cannot parse string: %s", ) } := .getValue() .t = typeRawString .s = return , , nil }if [0] == 't' {iflen() < len("true") || [:len("true")] != "true" {returnnil, , fmt.Errorf("unexpected value found: %q", ) }returnvalueTrue, [len("true"):], nil }if [0] == 'f' {iflen() < len("false") || [:len("false")] != "false" {returnnil, , fmt.Errorf("unexpected value found: %q", ) }returnvalueFalse, [len("false"):], nil }if [0] == 'n' {iflen() < len("null") || [:len("null")] != "null" {// Try parsing NaNiflen() >= 3 && strings.EqualFold([:3], "nan") { := .getValue() .t = TypeNumber .s = [:3]return , [3:], nil }returnnil, , fmt.Errorf("unexpected value found: %q", ) }returnvalueNull, [len("null"):], nil } , , := parseRawNumber()if != nil {returnnil, , fmt.Errorf("cannot parse number: %s", ) } := .getValue() .t = TypeNumber .s = return , , nil}func ( string, *cache, int) (*Value, string, error) { = skipWS()iflen() == 0 {returnnil, , fmt.Errorf("missing ']'") }if [0] == ']' { := .getValue() .t = TypeArray .a = .a[:0]return , [1:], nil } := .getValue() .t = TypeArray .a = .a[:0]for {var *Valuevarerror = skipWS() , , = parseValue(, , )if != nil {returnnil, , fmt.Errorf("cannot parse array value: %s", ) } .a = append(.a, ) = skipWS()iflen() == 0 {returnnil, , fmt.Errorf("unexpected end of array") }if [0] == ',' { = [1:]continue }if [0] == ']' { = [1:]return , , nil }returnnil, , fmt.Errorf("missing ',' after array value") }}func ( string, *cache, int) (*Value, string, error) { = skipWS()iflen() == 0 {returnnil, , fmt.Errorf("missing '}'") }if [0] == '}' { := .getValue() .t = TypeObject .o.reset()return , [1:], nil } := .getValue() .t = TypeObject .o.reset()for {varerror := .o.getKV()// Parse key. = skipWS()iflen() == 0 || [0] != '"' {returnnil, , fmt.Errorf(`cannot find opening '"" for object key`) } .k, , = parseRawKey([1:])if != nil {returnnil, , fmt.Errorf("cannot parse object key: %s", ) } = skipWS()iflen() == 0 || [0] != ':' {returnnil, , fmt.Errorf("missing ':' after object key") } = [1:]// Parse value = skipWS() .v, , = parseValue(, , )if != nil {returnnil, , fmt.Errorf("cannot parse object value: %s", ) } = skipWS()iflen() == 0 {returnnil, , fmt.Errorf("unexpected end of object") }if [0] == ',' { = [1:]continue }if [0] == '}' {return , [1:], nil }returnnil, , fmt.Errorf("missing ',' after object value") }}func ( []byte, string) []byte {if !hasSpecialChars() {// Fast path - nothing to escape. = append(, '"') = append(, ...) = append(, '"')return }// Slow path.returnstrconv.AppendQuote(, )}func ( string) bool {ifstrings.IndexByte(, '"') >= 0 || strings.IndexByte(, '\\') >= 0 {returntrue }for := 0; < len(); ++ {if [] < 0x20 {returntrue } }returnfalse}func ( string) string { := strings.IndexByte(, '\\')if < 0 {// Fast path - nothing to unescape.return }// Slow path - unescape string. := s2b() // It is safe to do, since s points to a byte slice in Parser.b. = [:] = [+1:]forlen() > 0 { := [0] = [1:]switch {case'"': = append(, '"')case'\\': = append(, '\\')case'/': = append(, '/')case'b': = append(, '\b')case'f': = append(, '\f')case'n': = append(, '\n')case'r': = append(, '\r')case't': = append(, '\t')case'u':iflen() < 4 {// Too short escape sequence. Just store it unchanged. = append(, "\\u"...)break } := [:4] , := strconv.ParseUint(, 16, 16)if != nil {// Invalid escape sequence. Just store it unchanged. = append(, "\\u"...)break } = [4:]if !utf16.IsSurrogate(rune()) { = append(, string(rune())...)break }// Surrogate. // See https://en.wikipedia.org/wiki/Universal_Character_Set_characters#Surrogatesiflen() < 6 || [0] != '\\' || [1] != 'u' { = append(, "\\u"...) = append(, ...)break } , := strconv.ParseUint([2:6], 16, 16)if != nil { = append(, "\\u"...) = append(, ...)break } := utf16.DecodeRune(rune(), rune()) = append(, string()...) = [6:]default:// Unknown escape sequence. Just store it unchanged. = append(, '\\', ) } = strings.IndexByte(, '\\')if < 0 { = append(, ...)break } = append(, [:]...) = [+1:] }returnb2s()}// parseRawKey is similar to parseRawString, but is optimized// for small-sized keys without escape sequences.func ( string) (string, string, error) {for := 0; < len(); ++ {if [] == '"' {// Fast path.return [:], [+1:], nil }if [] == '\\' {// Slow path.returnparseRawString() } }return , "", fmt.Errorf(`missing closing '"'`)}func ( string) (string, string, error) { := strings.IndexByte(, '"')if < 0 {return , "", fmt.Errorf(`missing closing '"'`) }if == 0 || [-1] != '\\' {// Fast path. No escaped ".return [:], [+1:], nil }// Slow path - possible escaped " found. := for { := - 1for > 0 && [-1] == '\\' { -- }ifuint(-)%2 == 0 {return [:len()-len()+], [+1:], nil } = [+1:] = strings.IndexByte(, '"')if < 0 {return , "", fmt.Errorf(`missing closing '"'`) }if == 0 || [-1] != '\\' {return [:len()-len()+], [+1:], nil } }}func ( string) (string, string, error) {// The caller must ensure len(s) > 0// Find the end of the number.for := 0; < len(); ++ { := []if ( >= '0' && <= '9') || == '.' || == '-' || == 'e' || == 'E' || == '+' {continue }if == 0 || == 1 && ([0] == '-' || [0] == '+') {iflen([:]) >= 3 { := [ : +3]ifstrings.EqualFold(, "inf") || strings.EqualFold(, "nan") {return [:+3], [+3:], nil } }return"", , fmt.Errorf("unexpected char: %q", [:1]) } := [:] = [:]return , , nil }return , "", nil}// Object represents JSON object.//// Object cannot be used from concurrent goroutines.// Use per-goroutine parsers or ParserPool instead.typeObjectstruct {kvs []kvkeysUnescapedbool}func ( *Object) () { .kvs = .kvs[:0] .keysUnescaped = false}// MarshalTo appends marshaled o to dst and returns the result.func ( *Object) ( []byte) []byte { = append(, '{')for , := range .kvs {if .keysUnescaped { = escapeString(, .k) } else { = append(, '"') = append(, .k...) = append(, '"') } = append(, ':') = .v.MarshalTo()if != len(.kvs)-1 { = append(, ',') } } = append(, '}')return}// String returns string representation for the o.//// This function is for debugging purposes only. It isn't optimized for speed.// See MarshalTo instead.func ( *Object) () string { := .MarshalTo(nil)// It is safe converting b to string without allocation, since b is no longer // reachable after this line.returnb2s()}func ( *Object) () *kv {ifcap(.kvs) > len(.kvs) { .kvs = .kvs[:len(.kvs)+1] } else { .kvs = append(.kvs, kv{}) }return &.kvs[len(.kvs)-1]}func ( *Object) () {if .keysUnescaped {return } := .kvsfor := range { := &[] .k = unescapeStringBestEffort(.k) } .keysUnescaped = true}// Len returns the number of items in the o.func ( *Object) () int {returnlen(.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 ( *Object) ( string) *Value {if !.keysUnescaped && strings.IndexByte(, '\\') < 0 {// Fast path - try searching for the key without object keys unescaping.for , := range .kvs {if .k == {return .v } } }// Slow path - unescape object keys. .unescapeKeys()for , := range .kvs {if .k == {return .v } }returnnil}// 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 ( *Object) ( func( []byte, *Value)) {if == nil {return } .unescapeKeys()for , := range .kvs { (s2b(.k), .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.typeValuestruct {oObjecta []*ValuesstringtType}// MarshalTo appends marshaled v to dst and returns the result.func ( *Value) ( []byte) []byte {switch .t {casetypeRawString: = append(, '"') = append(, .s...) = append(, '"')returncaseTypeObject:return .o.MarshalTo()caseTypeArray: = append(, '[')for , := range .a { = .()if != len(.a)-1 { = append(, ',') } } = append(, ']')returncaseTypeString:returnescapeString(, .s)caseTypeNumber:returnappend(, .s...)caseTypeTrue:returnappend(, "true"...)caseTypeFalse:returnappend(, "false"...)caseTypeNull:returnappend(, "null"...)default:panic(fmt.Errorf("BUG: unexpected Value type: %d", .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 ( *Value) () string { := .MarshalTo(nil)// It is safe converting b to string without allocation, since b is no longer // reachable after this line.returnb2s()}// Type represents JSON type.typeTypeintconst (// TypeNull is JSON null.TypeNullType = 0// TypeObject is JSON object type.TypeObjectType = 1// TypeArray is JSON array type.TypeArrayType = 2// TypeString is JSON string type.TypeStringType = 3// TypeNumber is JSON number type.TypeNumberType = 4// TypeTrue is JSON true.TypeTrueType = 5// TypeFalse is JSON false.TypeFalseType = 6typeRawStringType = 7)// String returns string representation of t.func ( Type) () string {switch {caseTypeObject:return"object"caseTypeArray:return"array"caseTypeString:return"string"caseTypeNumber:return"number"caseTypeTrue:return"true"caseTypeFalse:return"false"caseTypeNull:return"null"// typeRawString is skipped intentionally, // since it shouldn't be visible to user.default:panic(fmt.Errorf("BUG: unknown Value type: %d", )) }}// Type returns the type of the v.func ( *Value) () Type {if .t == typeRawString { .s = unescapeStringBestEffort(.s) .t = TypeString }return .t}// Exists returns true if the field exists for the given keys path.//// Array indexes may be represented as decimal numbers in keys.func ( *Value) ( ...string) bool { = .Get(...)return != 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 ( *Value) ( ...string) *Value {if == nil {returnnil }for , := range {if .t == TypeObject { = .o.Get()if == nil {returnnil } } elseif .t == TypeArray { , := strconv.Atoi()if != nil || < 0 || >= len(.a) {returnnil } = .a[] } else {returnnil } }return}// 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 ( *Value) ( ...string) *Object { = .Get(...)if == nil || .t != TypeObject {returnnil }return &.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 ( *Value) ( ...string) []*Value { = .Get(...)if == nil || .t != TypeArray {returnnil }return .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 ( *Value) ( ...string) float64 { = .Get(...)if == nil || .Type() != TypeNumber {return0 }returnfastfloat.ParseBestEffort(.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 ( *Value) ( ...string) int { = .Get(...)if == nil || .Type() != TypeNumber {return0 } := fastfloat.ParseInt64BestEffort(.s) := int()ifint64() != {return0 }return}// 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 ( *Value) ( ...string) uint { = .Get(...)if == nil || .Type() != TypeNumber {return0 } := fastfloat.ParseUint64BestEffort(.s) := uint()ifuint64() != {return0 }return}// 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 ( *Value) ( ...string) int64 { = .Get(...)if == nil || .Type() != TypeNumber {return0 }returnfastfloat.ParseInt64BestEffort(.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 ( *Value) ( ...string) uint64 { = .Get(...)if == nil || .Type() != TypeNumber {return0 }returnfastfloat.ParseUint64BestEffort(.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 ( *Value) ( ...string) []byte { = .Get(...)if == nil || .Type() != TypeString {returnnil }returns2b(.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 ( *Value) ( ...string) bool { = .Get(...)if != nil && .t == TypeTrue {returntrue }returnfalse}// 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 ( *Value) () (*Object, error) {if .t != TypeObject {returnnil, fmt.Errorf("value doesn't contain object; it contains %s", .Type()) }return &.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 ( *Value) () ([]*Value, error) {if .t != TypeArray {returnnil, fmt.Errorf("value doesn't contain array; it contains %s", .Type()) }return .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 ( *Value) () ([]byte, error) {if .Type() != TypeString {returnnil, fmt.Errorf("value doesn't contain string; it contains %s", .Type()) }returns2b(.s), nil}// Float64 returns the underlying JSON number for the v.//// Use GetFloat64 if you don't need error handling.func ( *Value) () (float64, error) {if .Type() != TypeNumber {return0, fmt.Errorf("value doesn't contain number; it contains %s", .Type()) }returnfastfloat.Parse(.s)}// Int returns the underlying JSON int for the v.//// Use GetInt if you don't need error handling.func ( *Value) () (int, error) {if .Type() != TypeNumber {return0, fmt.Errorf("value doesn't contain number; it contains %s", .Type()) } , := fastfloat.ParseInt64(.s)if != nil {return0, } := int()ifint64() != {return0, fmt.Errorf("number %q doesn't fit int", .s) }return , nil}// Uint returns the underlying JSON uint for the v.//// Use GetInt if you don't need error handling.func ( *Value) () (uint, error) {if .Type() != TypeNumber {return0, fmt.Errorf("value doesn't contain number; it contains %s", .Type()) } , := fastfloat.ParseUint64(.s)if != nil {return0, } := uint()ifuint64() != {return0, fmt.Errorf("number %q doesn't fit uint", .s) }return , nil}// Int64 returns the underlying JSON int64 for the v.//// Use GetInt64 if you don't need error handling.func ( *Value) () (int64, error) {if .Type() != TypeNumber {return0, fmt.Errorf("value doesn't contain number; it contains %s", .Type()) }returnfastfloat.ParseInt64(.s)}// Uint64 returns the underlying JSON uint64 for the v.//// Use GetInt64 if you don't need error handling.func ( *Value) () (uint64, error) {if .Type() != TypeNumber {return0, fmt.Errorf("value doesn't contain number; it contains %s", .Type()) }returnfastfloat.ParseUint64(.s)}// Bool returns the underlying JSON bool for the v.//// Use GetBool if you don't need error handling.func ( *Value) () (bool, error) {if .t == TypeTrue {returntrue, nil }if .t == TypeFalse {returnfalse, nil }returnfalse, fmt.Errorf("value doesn't contain bool; it contains %s", .Type())}var (valueTrue = &Value{t: TypeTrue}valueFalse = &Value{t: TypeFalse}valueNull = &Value{t: TypeNull})
The pages are generated with Goldsv0.4.9. (GOOS=linux GOARCH=amd64)