internal/encoding/json: initial commit of JSON parser/serializer
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>
diff --git a/internal/encoding/json/value.go b/internal/encoding/json/value.go
new file mode 100644
index 0000000..aeff222
--- /dev/null
+++ b/internal/encoding/json/value.go
@@ -0,0 +1,206 @@
+// 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
+}