// Copyright 2018 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package jsonimport ()// kind represents an encoding type.type kind uint8const ( _ kind = (1 << iota) / 2 name scalar objectOpen objectClose arrayOpen arrayClose)// Encoder provides methods to write out JSON constructs and values. The user is// responsible for producing valid sequences of JSON constructs and values.typeEncoderstruct { indent string lastKind kind indents []byte out []byte}// NewEncoder returns an Encoder.//// If indent is a non-empty string, it causes every entry for an Array or Object// to be preceded by the indent and trailed by a newline.func ( []byte, string) (*Encoder, error) { := &Encoder{out: , }iflen() > 0 {ifstrings.Trim(, " \t") != "" {returnnil, errors.New("indent may only be composed of space or tab characters") } .indent = }return , nil}// Bytes returns the content of the written bytes.func ( *Encoder) () []byte {return .out}// WriteNull writes out the null value.func ( *Encoder) () { .prepareNext(scalar) .out = append(.out, "null"...)}// WriteBool writes out the given boolean value.func ( *Encoder) ( bool) { .prepareNext(scalar)if { .out = append(.out, "true"...) } else { .out = append(.out, "false"...) }}// WriteString writes out the given string in JSON string value. Returns error// if input string contains invalid UTF-8.func ( *Encoder) ( string) error { .prepareNext(scalar)varerrorif .out, = appendString(.out, ); != nil {return }returnnil}// Sentinel error used for indicating invalid UTF-8.var errInvalidUTF8 = errors.New("invalid UTF-8")func appendString( []byte, string) ([]byte, error) { = append(, '"') := indexNeedEscapeInString() , = [:], append(, [:]...)forlen() > 0 {switch , := utf8.DecodeRuneInString(); {case == utf8.RuneError && == 1:return , errInvalidUTF8case < ' ' || == '"' || == '\\': = append(, '\\')switch {case'"', '\\': = append(, byte())case'\b': = append(, 'b')case'\f': = append(, 'f')case'\n': = append(, 'n')case'\r': = append(, 'r')case'\t': = append(, 't')default: = append(, 'u') = append(, "0000"[1+(bits.Len32(uint32())-1)/4:]...) = strconv.AppendUint(, uint64(), 16) } = [:]default: := indexNeedEscapeInString([:]) , = [+:], append(, [:+]...) } } = append(, '"')return , nil}// indexNeedEscapeInString returns the index of the character that needs// escaping. If no characters need escaping, this returns the input length.func indexNeedEscapeInString( string) int {for , := range {if < ' ' || == '\\' || == '"' || == utf8.RuneError {return } }returnlen()}// WriteFloat writes out the given float and bitSize in JSON number value.func ( *Encoder) ( float64, int) { .prepareNext(scalar) .out = appendFloat(.out, , )}// appendFloat formats given float in bitSize, and appends to the given []byte.func appendFloat( []byte, float64, int) []byte {switch {casemath.IsNaN():returnappend(, `"NaN"`...)casemath.IsInf(, +1):returnappend(, `"Infinity"`...)casemath.IsInf(, -1):returnappend(, `"-Infinity"`...) }// JSON number formatting logic based on encoding/json. // See floatEncoder.encode for reference. := byte('f')if := math.Abs(); != 0 {if == 64 && ( < 1e-6 || >= 1e21) || == 32 && (float32() < 1e-6 || float32() >= 1e21) { = 'e' } } = strconv.AppendFloat(, , , -1, )if == 'e' { := len()if >= 4 && [-4] == 'e' && [-3] == '-' && [-2] == '0' { [-2] = [-1] = [:-1] } }return}// WriteInt writes out the given signed integer in JSON number value.func ( *Encoder) ( int64) { .prepareNext(scalar) .out = strconv.AppendInt(.out, , 10)}// WriteUint writes out the given unsigned integer in JSON number value.func ( *Encoder) ( uint64) { .prepareNext(scalar) .out = strconv.AppendUint(.out, , 10)}// StartObject writes out the '{' symbol.func ( *Encoder) () { .prepareNext(objectOpen) .out = append(.out, '{')}// EndObject writes out the '}' symbol.func ( *Encoder) () { .prepareNext(objectClose) .out = append(.out, '}')}// WriteName writes out the given string in JSON string value and the name// separator ':'. Returns error if input string contains invalid UTF-8, which// should not be likely as protobuf field names should be valid.func ( *Encoder) ( string) error { .prepareNext(name)varerror// Append to output regardless of error. .out, = appendString(.out, ) .out = append(.out, ':')return}// StartArray writes out the '[' symbol.func ( *Encoder) () { .prepareNext(arrayOpen) .out = append(.out, '[')}// EndArray writes out the ']' symbol.func ( *Encoder) () { .prepareNext(arrayClose) .out = append(.out, ']')}// prepareNext adds possible comma and indentation for the next value based// on last type and indent option. It also updates lastKind to next.func ( *Encoder) ( kind) {deferfunc() {// Set lastKind to next. .lastKind = }()iflen(.indent) == 0 {// Need to add comma on the following condition.if .lastKind&(scalar|objectClose|arrayClose) != 0 && &(name|scalar|objectOpen|arrayOpen) != 0 { .out = append(.out, ',')// For single-line output, add a random extra space after each // comma to make output unstable.ifdetrand.Bool() { .out = append(.out, ' ') } }return }switch {case .lastKind&(objectOpen|arrayOpen) != 0:// If next type is NOT closing, add indent and newline.if &(objectClose|arrayClose) == 0 { .indents = append(.indents, .indent...) .out = append(.out, '\n') .out = append(.out, .indents...) }case .lastKind&(scalar|objectClose|arrayClose) != 0:switch {// If next type is either a value or name, add comma and newline.case &(name|scalar|objectOpen|arrayOpen) != 0: .out = append(.out, ',', '\n')// If next type is a closing object or array, adjust indentation.case &(objectClose|arrayClose) != 0: .indents = .indents[:len(.indents)-len(.indent)] .out = append(.out, '\n') } .out = append(.out, .indents...)case .lastKind&name != 0: .out = append(.out, ' ')// For multi-line output, add a random extra space after key: to make // output unstable.ifdetrand.Bool() { .out = append(.out, ' ') } }}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.