blob: aeff22225f18adfc34f0e88c23df8e8108e5106c [file] [log] [blame]
// 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
}