mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 04:42:31 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			559 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			559 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2015 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 jsonpb
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"math"
 | |
| 	"reflect"
 | |
| 	"sort"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/golang/protobuf/proto"
 | |
| 	"google.golang.org/protobuf/encoding/protojson"
 | |
| 	protoV2 "google.golang.org/protobuf/proto"
 | |
| 	"google.golang.org/protobuf/reflect/protoreflect"
 | |
| 	"google.golang.org/protobuf/reflect/protoregistry"
 | |
| )
 | |
| 
 | |
| const wrapJSONMarshalV2 = false
 | |
| 
 | |
| // Marshaler is a configurable object for marshaling protocol buffer messages
 | |
| // to the specified JSON representation.
 | |
| type Marshaler struct {
 | |
| 	// OrigName specifies whether to use the original protobuf name for fields.
 | |
| 	OrigName bool
 | |
| 
 | |
| 	// EnumsAsInts specifies whether to render enum values as integers,
 | |
| 	// as opposed to string values.
 | |
| 	EnumsAsInts bool
 | |
| 
 | |
| 	// EmitDefaults specifies whether to render fields with zero values.
 | |
| 	EmitDefaults bool
 | |
| 
 | |
| 	// Indent controls whether the output is compact or not.
 | |
| 	// If empty, the output is compact JSON. Otherwise, every JSON object
 | |
| 	// entry and JSON array value will be on its own line.
 | |
| 	// Each line will be preceded by repeated copies of Indent, where the
 | |
| 	// number of copies is the current indentation depth.
 | |
| 	Indent string
 | |
| 
 | |
| 	// AnyResolver is used to resolve the google.protobuf.Any well-known type.
 | |
| 	// If unset, the global registry is used by default.
 | |
| 	AnyResolver AnyResolver
 | |
| }
 | |
| 
 | |
| // JSONPBMarshaler is implemented by protobuf messages that customize the
 | |
| // way they are marshaled to JSON. Messages that implement this should also
 | |
| // implement JSONPBUnmarshaler so that the custom format can be parsed.
 | |
| //
 | |
| // The JSON marshaling must follow the proto to JSON specification:
 | |
| //	https://developers.google.com/protocol-buffers/docs/proto3#json
 | |
| //
 | |
| // Deprecated: Custom types should implement protobuf reflection instead.
 | |
| type JSONPBMarshaler interface {
 | |
| 	MarshalJSONPB(*Marshaler) ([]byte, error)
 | |
| }
 | |
| 
 | |
| // Marshal serializes a protobuf message as JSON into w.
 | |
| func (jm *Marshaler) Marshal(w io.Writer, m proto.Message) error {
 | |
| 	b, err := jm.marshal(m)
 | |
| 	if len(b) > 0 {
 | |
| 		if _, err := w.Write(b); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // MarshalToString serializes a protobuf message as JSON in string form.
 | |
| func (jm *Marshaler) MarshalToString(m proto.Message) (string, error) {
 | |
| 	b, err := jm.marshal(m)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	return string(b), nil
 | |
| }
 | |
| 
 | |
| func (jm *Marshaler) marshal(m proto.Message) ([]byte, error) {
 | |
| 	v := reflect.ValueOf(m)
 | |
| 	if m == nil || (v.Kind() == reflect.Ptr && v.IsNil()) {
 | |
| 		return nil, errors.New("Marshal called with nil")
 | |
| 	}
 | |
| 
 | |
| 	// Check for custom marshalers first since they may not properly
 | |
| 	// implement protobuf reflection that the logic below relies on.
 | |
| 	if jsm, ok := m.(JSONPBMarshaler); ok {
 | |
| 		return jsm.MarshalJSONPB(jm)
 | |
| 	}
 | |
| 
 | |
| 	if wrapJSONMarshalV2 {
 | |
| 		opts := protojson.MarshalOptions{
 | |
| 			UseProtoNames:   jm.OrigName,
 | |
| 			UseEnumNumbers:  jm.EnumsAsInts,
 | |
| 			EmitUnpopulated: jm.EmitDefaults,
 | |
| 			Indent:          jm.Indent,
 | |
| 		}
 | |
| 		if jm.AnyResolver != nil {
 | |
| 			opts.Resolver = anyResolver{jm.AnyResolver}
 | |
| 		}
 | |
| 		return opts.Marshal(proto.MessageReflect(m).Interface())
 | |
| 	} else {
 | |
| 		// Check for unpopulated required fields first.
 | |
| 		m2 := proto.MessageReflect(m)
 | |
| 		if err := protoV2.CheckInitialized(m2.Interface()); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		w := jsonWriter{Marshaler: jm}
 | |
| 		err := w.marshalMessage(m2, "", "")
 | |
| 		return w.buf, err
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type jsonWriter struct {
 | |
| 	*Marshaler
 | |
| 	buf []byte
 | |
| }
 | |
| 
 | |
| func (w *jsonWriter) write(s string) {
 | |
| 	w.buf = append(w.buf, s...)
 | |
| }
 | |
| 
 | |
| func (w *jsonWriter) marshalMessage(m protoreflect.Message, indent, typeURL string) error {
 | |
| 	if jsm, ok := proto.MessageV1(m.Interface()).(JSONPBMarshaler); ok {
 | |
| 		b, err := jsm.MarshalJSONPB(w.Marshaler)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if typeURL != "" {
 | |
| 			// we are marshaling this object to an Any type
 | |
| 			var js map[string]*json.RawMessage
 | |
| 			if err = json.Unmarshal(b, &js); err != nil {
 | |
| 				return fmt.Errorf("type %T produced invalid JSON: %v", m.Interface(), err)
 | |
| 			}
 | |
| 			turl, err := json.Marshal(typeURL)
 | |
| 			if err != nil {
 | |
| 				return fmt.Errorf("failed to marshal type URL %q to JSON: %v", typeURL, err)
 | |
| 			}
 | |
| 			js["@type"] = (*json.RawMessage)(&turl)
 | |
| 			if b, err = json.Marshal(js); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 		w.write(string(b))
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	md := m.Descriptor()
 | |
| 	fds := md.Fields()
 | |
| 
 | |
| 	// Handle well-known types.
 | |
| 	const secondInNanos = int64(time.Second / time.Nanosecond)
 | |
| 	switch wellKnownType(md.FullName()) {
 | |
| 	case "Any":
 | |
| 		return w.marshalAny(m, indent)
 | |
| 	case "BoolValue", "BytesValue", "StringValue",
 | |
| 		"Int32Value", "UInt32Value", "FloatValue",
 | |
| 		"Int64Value", "UInt64Value", "DoubleValue":
 | |
| 		fd := fds.ByNumber(1)
 | |
| 		return w.marshalValue(fd, m.Get(fd), indent)
 | |
| 	case "Duration":
 | |
| 		const maxSecondsInDuration = 315576000000
 | |
| 		// "Generated output always contains 0, 3, 6, or 9 fractional digits,
 | |
| 		//  depending on required precision."
 | |
| 		s := m.Get(fds.ByNumber(1)).Int()
 | |
| 		ns := m.Get(fds.ByNumber(2)).Int()
 | |
| 		if s < -maxSecondsInDuration || s > maxSecondsInDuration {
 | |
| 			return fmt.Errorf("seconds out of range %v", s)
 | |
| 		}
 | |
| 		if ns <= -secondInNanos || ns >= secondInNanos {
 | |
| 			return fmt.Errorf("ns out of range (%v, %v)", -secondInNanos, secondInNanos)
 | |
| 		}
 | |
| 		if (s > 0 && ns < 0) || (s < 0 && ns > 0) {
 | |
| 			return errors.New("signs of seconds and nanos do not match")
 | |
| 		}
 | |
| 		var sign string
 | |
| 		if s < 0 || ns < 0 {
 | |
| 			sign, s, ns = "-", -1*s, -1*ns
 | |
| 		}
 | |
| 		x := fmt.Sprintf("%s%d.%09d", sign, s, ns)
 | |
| 		x = strings.TrimSuffix(x, "000")
 | |
| 		x = strings.TrimSuffix(x, "000")
 | |
| 		x = strings.TrimSuffix(x, ".000")
 | |
| 		w.write(fmt.Sprintf(`"%vs"`, x))
 | |
| 		return nil
 | |
| 	case "Timestamp":
 | |
| 		// "RFC 3339, where generated output will always be Z-normalized
 | |
| 		//  and uses 0, 3, 6 or 9 fractional digits."
 | |
| 		s := m.Get(fds.ByNumber(1)).Int()
 | |
| 		ns := m.Get(fds.ByNumber(2)).Int()
 | |
| 		if ns < 0 || ns >= secondInNanos {
 | |
| 			return fmt.Errorf("ns out of range [0, %v)", secondInNanos)
 | |
| 		}
 | |
| 		t := time.Unix(s, ns).UTC()
 | |
| 		// time.RFC3339Nano isn't exactly right (we need to get 3/6/9 fractional digits).
 | |
| 		x := t.Format("2006-01-02T15:04:05.000000000")
 | |
| 		x = strings.TrimSuffix(x, "000")
 | |
| 		x = strings.TrimSuffix(x, "000")
 | |
| 		x = strings.TrimSuffix(x, ".000")
 | |
| 		w.write(fmt.Sprintf(`"%vZ"`, x))
 | |
| 		return nil
 | |
| 	case "Value":
 | |
| 		// JSON value; which is a null, number, string, bool, object, or array.
 | |
| 		od := md.Oneofs().Get(0)
 | |
| 		fd := m.WhichOneof(od)
 | |
| 		if fd == nil {
 | |
| 			return errors.New("nil Value")
 | |
| 		}
 | |
| 		return w.marshalValue(fd, m.Get(fd), indent)
 | |
| 	case "Struct", "ListValue":
 | |
| 		// JSON object or array.
 | |
| 		fd := fds.ByNumber(1)
 | |
| 		return w.marshalValue(fd, m.Get(fd), indent)
 | |
| 	}
 | |
| 
 | |
| 	w.write("{")
 | |
| 	if w.Indent != "" {
 | |
| 		w.write("\n")
 | |
| 	}
 | |
| 
 | |
| 	firstField := true
 | |
| 	if typeURL != "" {
 | |
| 		if err := w.marshalTypeURL(indent, typeURL); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		firstField = false
 | |
| 	}
 | |
| 
 | |
| 	for i := 0; i < fds.Len(); {
 | |
| 		fd := fds.Get(i)
 | |
| 		if od := fd.ContainingOneof(); od != nil {
 | |
| 			fd = m.WhichOneof(od)
 | |
| 			i += od.Fields().Len()
 | |
| 			if fd == nil {
 | |
| 				continue
 | |
| 			}
 | |
| 		} else {
 | |
| 			i++
 | |
| 		}
 | |
| 
 | |
| 		v := m.Get(fd)
 | |
| 
 | |
| 		if !m.Has(fd) {
 | |
| 			if !w.EmitDefaults || fd.ContainingOneof() != nil {
 | |
| 				continue
 | |
| 			}
 | |
| 			if fd.Cardinality() != protoreflect.Repeated && (fd.Message() != nil || fd.Syntax() == protoreflect.Proto2) {
 | |
| 				v = protoreflect.Value{} // use "null" for singular messages or proto2 scalars
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if !firstField {
 | |
| 			w.writeComma()
 | |
| 		}
 | |
| 		if err := w.marshalField(fd, v, indent); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		firstField = false
 | |
| 	}
 | |
| 
 | |
| 	// Handle proto2 extensions.
 | |
| 	if md.ExtensionRanges().Len() > 0 {
 | |
| 		// Collect a sorted list of all extension descriptor and values.
 | |
| 		type ext struct {
 | |
| 			desc protoreflect.FieldDescriptor
 | |
| 			val  protoreflect.Value
 | |
| 		}
 | |
| 		var exts []ext
 | |
| 		m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
 | |
| 			if fd.IsExtension() {
 | |
| 				exts = append(exts, ext{fd, v})
 | |
| 			}
 | |
| 			return true
 | |
| 		})
 | |
| 		sort.Slice(exts, func(i, j int) bool {
 | |
| 			return exts[i].desc.Number() < exts[j].desc.Number()
 | |
| 		})
 | |
| 
 | |
| 		for _, ext := range exts {
 | |
| 			if !firstField {
 | |
| 				w.writeComma()
 | |
| 			}
 | |
| 			if err := w.marshalField(ext.desc, ext.val, indent); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			firstField = false
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if w.Indent != "" {
 | |
| 		w.write("\n")
 | |
| 		w.write(indent)
 | |
| 	}
 | |
| 	w.write("}")
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *jsonWriter) writeComma() {
 | |
| 	if w.Indent != "" {
 | |
| 		w.write(",\n")
 | |
| 	} else {
 | |
| 		w.write(",")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (w *jsonWriter) marshalAny(m protoreflect.Message, indent string) error {
 | |
| 	// "If the Any contains a value that has a special JSON mapping,
 | |
| 	//  it will be converted as follows: {"@type": xxx, "value": yyy}.
 | |
| 	//  Otherwise, the value will be converted into a JSON object,
 | |
| 	//  and the "@type" field will be inserted to indicate the actual data type."
 | |
| 	md := m.Descriptor()
 | |
| 	typeURL := m.Get(md.Fields().ByNumber(1)).String()
 | |
| 	rawVal := m.Get(md.Fields().ByNumber(2)).Bytes()
 | |
| 
 | |
| 	var m2 protoreflect.Message
 | |
| 	if w.AnyResolver != nil {
 | |
| 		mi, err := w.AnyResolver.Resolve(typeURL)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		m2 = proto.MessageReflect(mi)
 | |
| 	} else {
 | |
| 		mt, err := protoregistry.GlobalTypes.FindMessageByURL(typeURL)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		m2 = mt.New()
 | |
| 	}
 | |
| 
 | |
| 	if err := protoV2.Unmarshal(rawVal, m2.Interface()); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if wellKnownType(m2.Descriptor().FullName()) == "" {
 | |
| 		return w.marshalMessage(m2, indent, typeURL)
 | |
| 	}
 | |
| 
 | |
| 	w.write("{")
 | |
| 	if w.Indent != "" {
 | |
| 		w.write("\n")
 | |
| 	}
 | |
| 	if err := w.marshalTypeURL(indent, typeURL); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	w.writeComma()
 | |
| 	if w.Indent != "" {
 | |
| 		w.write(indent)
 | |
| 		w.write(w.Indent)
 | |
| 		w.write(`"value": `)
 | |
| 	} else {
 | |
| 		w.write(`"value":`)
 | |
| 	}
 | |
| 	if err := w.marshalMessage(m2, indent+w.Indent, ""); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if w.Indent != "" {
 | |
| 		w.write("\n")
 | |
| 		w.write(indent)
 | |
| 	}
 | |
| 	w.write("}")
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *jsonWriter) marshalTypeURL(indent, typeURL string) error {
 | |
| 	if w.Indent != "" {
 | |
| 		w.write(indent)
 | |
| 		w.write(w.Indent)
 | |
| 	}
 | |
| 	w.write(`"@type":`)
 | |
| 	if w.Indent != "" {
 | |
| 		w.write(" ")
 | |
| 	}
 | |
| 	b, err := json.Marshal(typeURL)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	w.write(string(b))
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // marshalField writes field description and value to the Writer.
 | |
| func (w *jsonWriter) marshalField(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error {
 | |
| 	if w.Indent != "" {
 | |
| 		w.write(indent)
 | |
| 		w.write(w.Indent)
 | |
| 	}
 | |
| 	w.write(`"`)
 | |
| 	switch {
 | |
| 	case fd.IsExtension():
 | |
| 		// For message set, use the fname of the message as the extension name.
 | |
| 		name := string(fd.FullName())
 | |
| 		if isMessageSet(fd.ContainingMessage()) {
 | |
| 			name = strings.TrimSuffix(name, ".message_set_extension")
 | |
| 		}
 | |
| 
 | |
| 		w.write("[" + name + "]")
 | |
| 	case w.OrigName:
 | |
| 		name := string(fd.Name())
 | |
| 		if fd.Kind() == protoreflect.GroupKind {
 | |
| 			name = string(fd.Message().Name())
 | |
| 		}
 | |
| 		w.write(name)
 | |
| 	default:
 | |
| 		w.write(string(fd.JSONName()))
 | |
| 	}
 | |
| 	w.write(`":`)
 | |
| 	if w.Indent != "" {
 | |
| 		w.write(" ")
 | |
| 	}
 | |
| 	return w.marshalValue(fd, v, indent)
 | |
| }
 | |
| 
 | |
| func (w *jsonWriter) marshalValue(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error {
 | |
| 	switch {
 | |
| 	case fd.IsList():
 | |
| 		w.write("[")
 | |
| 		comma := ""
 | |
| 		lv := v.List()
 | |
| 		for i := 0; i < lv.Len(); i++ {
 | |
| 			w.write(comma)
 | |
| 			if w.Indent != "" {
 | |
| 				w.write("\n")
 | |
| 				w.write(indent)
 | |
| 				w.write(w.Indent)
 | |
| 				w.write(w.Indent)
 | |
| 			}
 | |
| 			if err := w.marshalSingularValue(fd, lv.Get(i), indent+w.Indent); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			comma = ","
 | |
| 		}
 | |
| 		if w.Indent != "" {
 | |
| 			w.write("\n")
 | |
| 			w.write(indent)
 | |
| 			w.write(w.Indent)
 | |
| 		}
 | |
| 		w.write("]")
 | |
| 		return nil
 | |
| 	case fd.IsMap():
 | |
| 		kfd := fd.MapKey()
 | |
| 		vfd := fd.MapValue()
 | |
| 		mv := v.Map()
 | |
| 
 | |
| 		// Collect a sorted list of all map keys and values.
 | |
| 		type entry struct{ key, val protoreflect.Value }
 | |
| 		var entries []entry
 | |
| 		mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
 | |
| 			entries = append(entries, entry{k.Value(), v})
 | |
| 			return true
 | |
| 		})
 | |
| 		sort.Slice(entries, func(i, j int) bool {
 | |
| 			switch kfd.Kind() {
 | |
| 			case protoreflect.BoolKind:
 | |
| 				return !entries[i].key.Bool() && entries[j].key.Bool()
 | |
| 			case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
 | |
| 				return entries[i].key.Int() < entries[j].key.Int()
 | |
| 			case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
 | |
| 				return entries[i].key.Uint() < entries[j].key.Uint()
 | |
| 			case protoreflect.StringKind:
 | |
| 				return entries[i].key.String() < entries[j].key.String()
 | |
| 			default:
 | |
| 				panic("invalid kind")
 | |
| 			}
 | |
| 		})
 | |
| 
 | |
| 		w.write(`{`)
 | |
| 		comma := ""
 | |
| 		for _, entry := range entries {
 | |
| 			w.write(comma)
 | |
| 			if w.Indent != "" {
 | |
| 				w.write("\n")
 | |
| 				w.write(indent)
 | |
| 				w.write(w.Indent)
 | |
| 				w.write(w.Indent)
 | |
| 			}
 | |
| 
 | |
| 			s := fmt.Sprint(entry.key.Interface())
 | |
| 			b, err := json.Marshal(s)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			w.write(string(b))
 | |
| 
 | |
| 			w.write(`:`)
 | |
| 			if w.Indent != "" {
 | |
| 				w.write(` `)
 | |
| 			}
 | |
| 
 | |
| 			if err := w.marshalSingularValue(vfd, entry.val, indent+w.Indent); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			comma = ","
 | |
| 		}
 | |
| 		if w.Indent != "" {
 | |
| 			w.write("\n")
 | |
| 			w.write(indent)
 | |
| 			w.write(w.Indent)
 | |
| 		}
 | |
| 		w.write(`}`)
 | |
| 		return nil
 | |
| 	default:
 | |
| 		return w.marshalSingularValue(fd, v, indent)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (w *jsonWriter) marshalSingularValue(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error {
 | |
| 	switch {
 | |
| 	case !v.IsValid():
 | |
| 		w.write("null")
 | |
| 		return nil
 | |
| 	case fd.Message() != nil:
 | |
| 		return w.marshalMessage(v.Message(), indent+w.Indent, "")
 | |
| 	case fd.Enum() != nil:
 | |
| 		if fd.Enum().FullName() == "google.protobuf.NullValue" {
 | |
| 			w.write("null")
 | |
| 			return nil
 | |
| 		}
 | |
| 
 | |
| 		vd := fd.Enum().Values().ByNumber(v.Enum())
 | |
| 		if vd == nil || w.EnumsAsInts {
 | |
| 			w.write(strconv.Itoa(int(v.Enum())))
 | |
| 		} else {
 | |
| 			w.write(`"` + string(vd.Name()) + `"`)
 | |
| 		}
 | |
| 		return nil
 | |
| 	default:
 | |
| 		switch v.Interface().(type) {
 | |
| 		case float32, float64:
 | |
| 			switch {
 | |
| 			case math.IsInf(v.Float(), +1):
 | |
| 				w.write(`"Infinity"`)
 | |
| 				return nil
 | |
| 			case math.IsInf(v.Float(), -1):
 | |
| 				w.write(`"-Infinity"`)
 | |
| 				return nil
 | |
| 			case math.IsNaN(v.Float()):
 | |
| 				w.write(`"NaN"`)
 | |
| 				return nil
 | |
| 			}
 | |
| 		case int64, uint64:
 | |
| 			w.write(fmt.Sprintf(`"%d"`, v.Interface()))
 | |
| 			return nil
 | |
| 		}
 | |
| 
 | |
| 		b, err := json.Marshal(v.Interface())
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		w.write(string(b))
 | |
| 		return nil
 | |
| 	}
 | |
| }
 |