mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-02-22 12:39:52 +00:00
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>
207 lines
5.1 KiB
Go
207 lines
5.1 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 implements the JSON format.
|
|
// This package has no semantic understanding for protocol buffers and is only
|
|
// a parser and composer for the format.
|
|
//
|
|
// This follows RFC 7159, with some notable implementation specifics:
|
|
// * numbers that are out of range result in a decoding error
|
|
// * duplicate keys in objects are not rejected
|
|
//
|
|
// Reasons why the standard encoding/json package is not suitable:
|
|
// * information about duplicate keys is lost
|
|
// * invalid UTF-8 is silently coerced into utf8.RuneError
|
|
package json
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Type represents a type expressible in the JSON format.
|
|
type Type uint8
|
|
|
|
const (
|
|
_ Type = iota
|
|
// Null is the null literal (i.e., "null").
|
|
Null
|
|
// Bool is a boolean (i.e., "true" or "false").
|
|
Bool
|
|
// Number is a floating-point number (e.g., "1.234" or "1e100").
|
|
Number
|
|
// String is an escaped string (e.g., `"the quick brown fox"`).
|
|
String
|
|
// Array is an ordered list of values (e.g., `[0, "one", true]`).
|
|
Array
|
|
// Object is an ordered map of values (e.g., `{"key": null}`).
|
|
Object
|
|
)
|
|
|
|
func (t Type) String() string {
|
|
switch t {
|
|
case Null:
|
|
return "null"
|
|
case Bool:
|
|
return "bool"
|
|
case Number:
|
|
return "number"
|
|
case String:
|
|
return "string"
|
|
case Array:
|
|
return "array"
|
|
case Object:
|
|
return "object"
|
|
default:
|
|
return "<invalid>"
|
|
}
|
|
}
|
|
|
|
// Value contains a value of a given Type.
|
|
type Value struct {
|
|
typ Type
|
|
raw []byte // raw bytes of the serialized data
|
|
str string // only for String
|
|
num float64 // only for Bool or Number
|
|
arr []Value // only for Array
|
|
obj [][2]Value // only for Object
|
|
}
|
|
|
|
// ValueOf returns a Value for a given Go value:
|
|
// nil => Null
|
|
// bool => Bool
|
|
// int32, int64 => Number
|
|
// uint32, uint64 => Number
|
|
// float32, float64 => Number
|
|
// string, []byte => String
|
|
// []Value => Array
|
|
// [][2]Value => Object
|
|
//
|
|
// ValueOf panics if the Go type is not one of the above.
|
|
func ValueOf(v interface{}) Value {
|
|
switch v := v.(type) {
|
|
case nil:
|
|
return Value{typ: Null}
|
|
case bool:
|
|
if v {
|
|
return Value{typ: Bool, num: 1}
|
|
} else {
|
|
return Value{typ: Bool, num: 0}
|
|
}
|
|
case int32:
|
|
return Value{typ: Number, num: float64(v)}
|
|
case int64:
|
|
return Value{typ: Number, num: float64(v)} // possible loss of precision
|
|
case uint32:
|
|
return Value{typ: Number, num: float64(v)}
|
|
case uint64:
|
|
return Value{typ: Number, num: float64(v)} // possible loss of precision
|
|
case float32:
|
|
return Value{typ: Number, num: float64(v)}
|
|
case float64:
|
|
return Value{typ: Number, num: float64(v)}
|
|
case string:
|
|
return Value{typ: String, str: string(v)}
|
|
case []byte:
|
|
return Value{typ: String, str: string(v)}
|
|
case []Value:
|
|
return Value{typ: Array, arr: v}
|
|
case [][2]Value:
|
|
return Value{typ: Object, obj: v}
|
|
default:
|
|
panic(fmt.Sprintf("invalid type %T", v))
|
|
}
|
|
}
|
|
func rawValueOf(v interface{}, raw []byte) Value {
|
|
v2 := ValueOf(v)
|
|
v2.raw = raw
|
|
return v2
|
|
}
|
|
|
|
// Type is the type of the value.
|
|
func (v Value) Type() Type {
|
|
return v.typ
|
|
}
|
|
|
|
// Bool returns v as a bool and panics if it is not a Bool.
|
|
func (v Value) Bool() bool {
|
|
if v.typ != Bool {
|
|
panic("value is not a boolean")
|
|
}
|
|
return v.num != 0
|
|
}
|
|
|
|
// Number returns v as a float64 and panics if it is not a Number.
|
|
func (v Value) Number() float64 {
|
|
if v.typ != Number {
|
|
panic("value is not a number")
|
|
}
|
|
return v.num
|
|
}
|
|
|
|
// String returns v as a string if the Type is String.
|
|
// Otherwise, this returns a formatted string of v for debugging purposes.
|
|
//
|
|
// Since JSON strings must be UTF-8, the marshaler and unmarshaler will verify
|
|
// for UTF-8 correctness.
|
|
func (v Value) String() string {
|
|
if v.typ != String {
|
|
return v.stringValue()
|
|
}
|
|
return v.str
|
|
}
|
|
func (v Value) stringValue() string {
|
|
switch v.typ {
|
|
case Null, Bool, Number:
|
|
return string(v.Raw())
|
|
case Array:
|
|
var ss []string
|
|
for _, v := range v.Array() {
|
|
ss = append(ss, v.String())
|
|
}
|
|
return "[" + strings.Join(ss, ",") + "]"
|
|
case Object:
|
|
var ss []string
|
|
for _, v := range v.Object() {
|
|
ss = append(ss, v[0].String()+":"+v[1].String())
|
|
}
|
|
return "{" + strings.Join(ss, ",") + "}"
|
|
default:
|
|
return "<invalid>"
|
|
}
|
|
}
|
|
|
|
// Array returns the elements of v and panics if the Type is not Array.
|
|
// Mutations on the return value may not be observable from the Raw method.
|
|
func (v Value) Array() []Value {
|
|
if v.typ != Array {
|
|
panic("value is not an array")
|
|
}
|
|
return v.arr
|
|
}
|
|
|
|
// Object returns the items of v and panics if the Type is not Object.
|
|
// The [2]Value represents a key (of type String) and value pair.
|
|
//
|
|
// Mutations on the return value may not be observable from the Raw method.
|
|
func (v Value) Object() [][2]Value {
|
|
if v.typ != Object {
|
|
panic("value is not an object")
|
|
}
|
|
return v.obj
|
|
}
|
|
|
|
// Raw returns the raw representation of the value.
|
|
// The returned value may alias the input given to Unmarshal.
|
|
func (v Value) Raw() []byte {
|
|
if len(v.raw) > 0 {
|
|
return v.raw
|
|
}
|
|
p := encoder{}
|
|
if err := p.marshalValue(v); !p.nerr.Merge(err) {
|
|
return []byte("<invalid>")
|
|
}
|
|
return p.out
|
|
}
|