mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-01-04 02:38:50 +00:00
879b18d902
Package json provides a parser and serializer for the JSON format. This focuses on the grammar of the format and is agnostic towards specific semantics of protobuf types. High-level API: func Marshal(v Value, indent string) ([]byte, error) func Unmarshal(b []byte) (Value, error) type Type uint8 const Null Type ... type Value struct{ ... } func ValueOf(v interface{}) Value func (v Value) Type() Type func (v Value) Bool() bool func (v Value) Number() float64 func (v Value) String() string func (v Value) Array() []Value func (v Value) Object() [][2]Value func (v Value) Raw() []byte Change-Id: I26422f6b3881ef1a11b8aa95160645b1384b27b8 Reviewed-on: https://go-review.googlesource.com/127824 Reviewed-by: Herbie Ong <herbie@google.com>
80 lines
2.0 KiB
Go
80 lines
2.0 KiB
Go
// 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 json
|
|
|
|
import (
|
|
"io"
|
|
"math"
|
|
"regexp"
|
|
"strconv"
|
|
|
|
"google.golang.org/proto/internal/errors"
|
|
)
|
|
|
|
// marshalNumber encodes v as a Number.
|
|
func (p *encoder) marshalNumber(v Value) error {
|
|
var err error
|
|
p.out, err = appendNumber(p.out, v)
|
|
return err
|
|
}
|
|
func appendNumber(out []byte, v Value) ([]byte, error) {
|
|
if v.Type() != Number {
|
|
return nil, errors.New("invalid type %v, expected number", v.Type())
|
|
}
|
|
if len(v.raw) > 0 {
|
|
return append(out, v.raw...), nil
|
|
}
|
|
n := v.Number()
|
|
if math.IsInf(n, 0) || math.IsNaN(n) {
|
|
return nil, errors.New("invalid number value: %v", n)
|
|
}
|
|
|
|
// JSON number formatting logic based on encoding/json.
|
|
// See floatEncoder.encode for reference.
|
|
bits := 64
|
|
if float64(float32(n)) == n {
|
|
bits = 32
|
|
}
|
|
fmt := byte('f')
|
|
if abs := math.Abs(n); abs != 0 {
|
|
if bits == 64 && (abs < 1e-6 || abs >= 1e21) || bits == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
|
|
fmt = 'e'
|
|
}
|
|
}
|
|
out = strconv.AppendFloat(out, n, fmt, -1, bits)
|
|
if fmt == 'e' {
|
|
n := len(out)
|
|
if n >= 4 && out[n-4] == 'e' && out[n-3] == '-' && out[n-2] == '0' {
|
|
out[n-2] = out[n-1]
|
|
out = out[:n-1]
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// Exact expression to match a JSON floating-point number.
|
|
// JSON's grammar for floats is more restrictive than Go's grammar.
|
|
var floatRegexp = regexp.MustCompile("^-?(0|[1-9][0-9]*)([.][0-9]+)?([eE][+-]?[0-9]+)?")
|
|
|
|
// unmarshalNumber decodes a Number from the input.
|
|
func (p *decoder) unmarshalNumber() (Value, error) {
|
|
v, n, err := consumeNumber(p.in)
|
|
p.consume(n)
|
|
return v, err
|
|
}
|
|
func consumeNumber(in []byte) (Value, int, error) {
|
|
if len(in) == 0 {
|
|
return Value{}, 0, io.ErrUnexpectedEOF
|
|
}
|
|
if n := matchWithDelim(floatRegexp, in); n > 0 {
|
|
v, err := strconv.ParseFloat(string(in[:n]), 64)
|
|
if err != nil {
|
|
return Value{}, 0, err
|
|
}
|
|
return rawValueOf(v, in[:n:n]), n, nil
|
|
}
|
|
return Value{}, 0, newSyntaxError("invalid %q as number", errRegexp.Find(in))
|
|
}
|