marshaler.go (111 lines of code) (raw):

// Copyright 2018 Elasticsearch BV // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package fastjson import ( "encoding/json" "errors" "fmt" "math" ) // Marshaler defines an interface that types can implement to provide // fast JSON marshaling. type Marshaler interface { // MarshalFastJSON writes a JSON representation of the type to w. // // MarshalFastJSON is expected to suppress any panics. Depending // on the application, it may be expected that MarshalFastJSON // writes valid JSON to w, even in error cases. // // The returned error will be propagated up through to callers of // fastjson.Marshal. MarshalFastJSON(w *Writer) error } // Appender defines an interface that types can implement to append // their JSON representation to a buffer. type Appender interface { // AppendJSON appends the JSON representation of the value to the // buffer, and returns the extended buffer. // // AppendJSON is required not to panic or fail. AppendJSON([]byte) []byte } // Marshal marshals v as JSON to w. // // For all basic types, Marshal uses w's methods to marshal the values // directly. If v implements Marshaler, its MarshalFastJSON method will // be used; if v implements Appender, its AppendJSON method will be used, // and it is assumed to append valid JSON. As a final resort, we use // json.Marshal. // // Where json.Marshal is used internally (see above), errors or panics // produced by json.Marshal will be encoded as JSON objects, with special keys // "__ERROR__" for errors, and "__PANIC__" for panics. e.g. if json.Marshal // panics due to a broken json.Marshaler implementation or assumption, then // Marshal will encode the panic as // // {"__PANIC__": "panic calling MarshalJSON for type Foo: reason"} // // Marshal returns the first error encountered. func Marshal(w *Writer, v interface{}) error { switch v := v.(type) { case nil: w.RawString("null") case string: w.String(v) case uint: w.Uint64(uint64(v)) case uint8: w.Uint64(uint64(v)) case uint16: w.Uint64(uint64(v)) case uint32: w.Uint64(uint64(v)) case uint64: w.Uint64(v) case int: w.Int64(int64(v)) case int8: w.Int64(int64(v)) case int16: w.Int64(int64(v)) case int32: w.Int64(int64(v)) case int64: w.Int64(v) case float32: if math.IsNaN(float64(v)) { return errors.New("json: unsupported value: NaN") } if math.IsInf(float64(v), 0) { return errors.New("json: unsupported value: Inf") } w.Float32(v) case float64: if math.IsNaN(v) { return errors.New("json: unsupported value: NaN") } if math.IsInf(v, 0) { return errors.New("json: unsupported value: Inf") } w.Float64(v) case bool: w.Bool(v) case map[string]interface{}: if v == nil { w.RawString("null") return nil } w.RawByte('{') var firstErr error first := true for k, v := range v { if first { first = false } else { w.RawByte(',') } w.String(k) w.RawByte(':') if err := Marshal(w, v); err != nil && firstErr == nil { firstErr = err } } w.RawByte('}') return firstErr case Marshaler: return v.MarshalFastJSON(w) case Appender: w.buf = v.AppendJSON(w.buf) default: return marshalReflect(w, v) } return nil } func marshalReflect(w *Writer, v interface{}) (result error) { defer func() { if r := recover(); r != nil { err, ok := r.(error) if !ok { err = fmt.Errorf("%s", r) } result = fmt.Errorf("panic calling MarshalJSON for type %T: %w", v, err) w.RawString(`{"__PANIC__":`) w.String(fmt.Sprint(result)) w.RawByte('}') } }() raw, err := json.Marshal(v) if err != nil { w.RawString(`{"__ERROR__":`) w.String(fmt.Sprint(err)) w.RawByte('}') return err } w.RawBytes(raw) return nil }