mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-01-06 00:55:51 +00:00
d2ece139c6
All unmarshaling error messages now contain line number and column information, except for the following errors: - `unexpected EOF` - `no support for proto1 MessageSets` - `required fields X not set` Changes to internal/encoding/json: - Moved encoding funcs in string.go and number.go into encode.go. - Separated out encoding kind constants from decoding ones. - Renamed file string.go to decode_string.go. - Renamed file number.go to decode_number.go. - Renamed Type struct to Kind. - Renamed Value struct to Token. - Token accessor methods no longer return error. Name, Bool, ParsedString will panic if called on the wrong kind. Float, Int, Uint has ok bool result to check against. - Changed Peek to return Token and error. Changes to encoding/protojson: - Updated internal/encoding/json API calls. - Added line info on most unmarshaling error messages and kept description simple and consistent. Change-Id: Ie50456694f2214c5c4fafd2c9b9239680da0deec Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/218978 Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
194 lines
4.4 KiB
Go
194 lines
4.4 KiB
Go
// Copyright 2019 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 (
|
|
"bytes"
|
|
"fmt"
|
|
"strconv"
|
|
)
|
|
|
|
// Kind represents a token kind expressible in the JSON format.
|
|
type Kind uint16
|
|
|
|
const (
|
|
Invalid Kind = (1 << iota) / 2
|
|
EOF
|
|
Null
|
|
Bool
|
|
Number
|
|
String
|
|
Name
|
|
ObjectOpen
|
|
ObjectClose
|
|
ArrayOpen
|
|
ArrayClose
|
|
|
|
// comma is only for parsing in between tokens and
|
|
// does not need to be exported.
|
|
comma
|
|
)
|
|
|
|
func (k Kind) String() string {
|
|
switch k {
|
|
case EOF:
|
|
return "eof"
|
|
case Null:
|
|
return "null"
|
|
case Bool:
|
|
return "bool"
|
|
case Number:
|
|
return "number"
|
|
case String:
|
|
return "string"
|
|
case ObjectOpen:
|
|
return "{"
|
|
case ObjectClose:
|
|
return "}"
|
|
case Name:
|
|
return "name"
|
|
case ArrayOpen:
|
|
return "["
|
|
case ArrayClose:
|
|
return "]"
|
|
case comma:
|
|
return ","
|
|
}
|
|
return "<invalid>"
|
|
}
|
|
|
|
// Token provides a parsed token kind and value.
|
|
//
|
|
// Values are provided by the difference accessor methods. The accessor methods
|
|
// Name, Bool, and ParsedString will panic if called on the wrong kind. There
|
|
// are different accessor methods for the Number kind for converting to the
|
|
// appropriate Go numeric type and those methods have the ok return value.
|
|
type Token struct {
|
|
// Token kind.
|
|
kind Kind
|
|
// pos provides the position of the token in the original input.
|
|
pos int
|
|
// raw bytes of the serialized token.
|
|
// This is a subslice into the original input.
|
|
raw []byte
|
|
// boo is parsed boolean value.
|
|
boo bool
|
|
// str is parsed string value.
|
|
str string
|
|
}
|
|
|
|
// Kind returns the token kind.
|
|
func (t Token) Kind() Kind {
|
|
return t.kind
|
|
}
|
|
|
|
// RawString returns the read value in string.
|
|
func (t Token) RawString() string {
|
|
return string(t.raw)
|
|
}
|
|
|
|
// Pos returns the token position from the input.
|
|
func (t Token) Pos() int {
|
|
return t.pos
|
|
}
|
|
|
|
// Name returns the object name if token is Name, else it will return an error.
|
|
func (t Token) Name() string {
|
|
if t.kind == Name {
|
|
return t.str
|
|
}
|
|
panic(fmt.Sprintf("Token is not a Name: %v", t.RawString()))
|
|
}
|
|
|
|
// Bool returns the bool value if token kind is Bool, else it panics.
|
|
func (t Token) Bool() bool {
|
|
if t.kind == Bool {
|
|
return t.boo
|
|
}
|
|
panic(fmt.Sprintf("Token is not a Bool: %v", t.RawString()))
|
|
}
|
|
|
|
// ParsedString returns the string value for a JSON string token or the read
|
|
// value in string if token is not a string.
|
|
func (t Token) ParsedString() string {
|
|
if t.kind == String {
|
|
return t.str
|
|
}
|
|
panic(fmt.Sprintf("Token is not a String: %v", t.RawString()))
|
|
}
|
|
|
|
// Float returns the floating-point number if token kind is Number.
|
|
//
|
|
// The floating-point precision is specified by the bitSize parameter: 32 for
|
|
// float32 or 64 for float64. If bitSize=32, the result still has type float64,
|
|
// but it will be convertible to float32 without changing its value. It will
|
|
// return false if the number exceeds the floating point limits for given
|
|
// bitSize.
|
|
func (t Token) Float(bitSize int) (float64, bool) {
|
|
if t.kind != Number {
|
|
return 0, false
|
|
}
|
|
f, err := strconv.ParseFloat(t.RawString(), bitSize)
|
|
if err != nil {
|
|
return 0, false
|
|
}
|
|
return f, true
|
|
}
|
|
|
|
// Int returns the signed integer number if token is Number.
|
|
//
|
|
// The given bitSize specifies the integer type that the result must fit into.
|
|
// It returns false if the number is not an integer value or if the result
|
|
// exceeds the limits for given bitSize.
|
|
func (t Token) Int(bitSize int) (int64, bool) {
|
|
s, ok := t.getIntStr()
|
|
if !ok {
|
|
return 0, false
|
|
}
|
|
n, err := strconv.ParseInt(s, 10, bitSize)
|
|
if err != nil {
|
|
return 0, false
|
|
}
|
|
return n, true
|
|
}
|
|
|
|
// Uint returns the signed integer number if token is Number, else it will
|
|
// return an error.
|
|
//
|
|
// The given bitSize specifies the unsigned integer type that the result must
|
|
// fit into. It returns false if the number is not an unsigned integer value
|
|
// or if the result exceeds the limits for given bitSize.
|
|
func (t Token) Uint(bitSize int) (uint64, bool) {
|
|
s, ok := t.getIntStr()
|
|
if !ok {
|
|
return 0, false
|
|
}
|
|
n, err := strconv.ParseUint(s, 10, bitSize)
|
|
if err != nil {
|
|
return 0, false
|
|
}
|
|
return n, true
|
|
}
|
|
|
|
func (t Token) getIntStr() (string, bool) {
|
|
if t.kind != Number {
|
|
return "", false
|
|
}
|
|
parts, ok := parseNumberParts(t.raw)
|
|
if !ok {
|
|
return "", false
|
|
}
|
|
return normalizeToIntString(parts)
|
|
}
|
|
|
|
// TokenEquals returns true if given Tokens are equal, else false.
|
|
func TokenEquals(x, y Token) bool {
|
|
return x.kind == y.kind &&
|
|
x.pos == y.pos &&
|
|
bytes.Equal(x.raw, y.raw) &&
|
|
x.boo == y.boo &&
|
|
x.str == y.str
|
|
}
|