This repository has been archived on 2019-06-23. You can view files and clone it, but cannot push or open issues or pull requests.
mumbledj/vendor/github.com/antonholmquist/jason/jason.go
2016-06-21 09:05:20 -07:00

767 lines
17 KiB
Go

// Use of this source code is governed by a
// license that can be found in the LICENSE file.
// Jason is designed to be convenient for reading arbitrary JSON while still honoring the strictness of the language.
// Inspired by other libraries and improved to work well for common use cases.
// It currently focuses on reading JSON data rather than creating it.
//
// Examples
//
// JSON is a commonly used data transfer format, so usually the data you want to read comes either as bytes or as an io.Reader.
//
// Create an object from bytes:
// v, err := jason.NewObjectFromBytes(b)
// .. or from a net/http response body:
// v, err := jason.NewObjectFromReader(res.body)
//
// Read values
//
// Reading values is done with Get<Type>(keys ...) or the generic Get(keys ...).
// If the key path is invalid or the type doesn't match, it will return an error and the default value.
//
// name, err := v.GetString("name")
// age, err := v.GetNumber("age")
// verified, err := v.GetBoolean("verified")
// education, err := v.GetObject("education")
// friends, err := v.GetObjectArray("friends")
//
// Loop through array
//
// Getting an array is done by Get<Type>Array() or the generic GetValueArray(). It returns an error if the value at that keypath is null (or something else than the type).
//
// friends, err := person.GetObjectArray("friends")
// for _, friend := range friends {
// name, err := friend.GetString("name")
// age, err := friend.GetNumber("age")
// }
//
// Loop through keys of object
//
// Looping through an object is done by first getting it with `GetObject()` and then range on the Map().
// The GetObject() method returns an error if the value at that keypath is null (or something else than an object).
//
// person, err := person.GetObject("person")
// for key, value := range person.Map() {
// ...
// }
package jason
import (
"bytes"
"encoding/json"
"errors"
"io"
)
// Value represents an arbitrary JSON value.
// It may contain a bool, number, string, object, array or null.
type Value struct {
data interface{}
exists bool // Used to separate nil and non-existing values
}
// Object represents an object JSON object.
// It inherets from Value but with an additional method to access
// a map representation of it's content. It's useful when iterating.
type Object struct {
Value
m map[string]*Value
valid bool
}
// Returns the golang map.
// Needed when iterating through the values of the object.
func (v *Object) Map() map[string]*Value {
return v.m
}
// Creates a new value from an io.reader.
// Returns an error if the reader does not contain valid json.
// Useful for parsing the body of a net/http response.
// Example: NewFromReader(res.Body)
func NewValueFromReader(reader io.Reader) (*Value, error) {
j := new(Value)
d := json.NewDecoder(reader)
d.UseNumber()
err := d.Decode(&j.data)
return j, err
}
// Creates a new value from bytes.
// Returns an error if the bytes are not valid json.
func NewValueFromBytes(b []byte) (*Value, error) {
r := bytes.NewReader(b)
return NewValueFromReader(r)
}
func objectFromValue(v *Value, err error) (*Object, error) {
if err != nil {
return nil, err
}
o, err := v.Object()
if err != nil {
return nil, err
}
return o, nil
}
func NewObjectFromBytes(b []byte) (*Object, error) {
return objectFromValue(NewValueFromBytes(b))
}
func NewObjectFromReader(reader io.Reader) (*Object, error) {
return objectFromValue(NewValueFromReader(reader))
}
// Marshal into bytes.
func (v *Value) Marshal() ([]byte, error) {
return json.Marshal(v.data)
}
// Private Get
func (v *Value) get(key string) (*Value, error) {
// Assume this is an object
obj, err := v.Object()
if err == nil {
child, ok := obj.Map()[key]
if ok {
return child, nil
} else {
return nil, errors.New("key not found")
}
}
return nil, err
}
// Private get path
func (v *Value) getPath(keys []string) (*Value, error) {
current := v
var err error
for _, key := range keys {
current, err = current.get(key)
if err != nil {
return nil, err
}
}
return current, nil
}
// Gets the value at key path.
// Returns error if the value does not exist.
// Consider using the more specific Get<Type>(..) methods instead.
// Example:
// value, err := GetValue("address", "street")
func (v *Object) GetValue(keys ...string) (*Value, error) {
return v.getPath(keys)
}
// Gets the value at key path and attempts to typecast the value into an object.
// Returns error if the value is not a json object.
// Example:
// object, err := GetObject("person", "address")
func (v *Object) GetObject(keys ...string) (*Object, error) {
child, err := v.getPath(keys)
if err != nil {
return nil, err
} else {
obj, err := child.Object()
if err != nil {
return nil, err
} else {
return obj, nil
}
}
return nil, nil
}
// Gets the value at key path and attempts to typecast the value into a string.
// Returns error if the value is not a json string.
// Example:
// string, err := GetString("address", "street")
func (v *Object) GetString(keys ...string) (string, error) {
child, err := v.getPath(keys)
if err != nil {
return "", err
} else {
return child.String()
}
return "", nil
}
// Gets the value at key path and attempts to typecast the value into null.
// Returns error if the value is not json null.
// Example:
// err := GetNull("address", "street")
func (v *Object) GetNull(keys ...string) error {
child, err := v.getPath(keys)
if err != nil {
return err
}
return child.Null()
}
// Gets the value at key path and attempts to typecast the value into a number.
// Returns error if the value is not a json number.
// Example:
// n, err := GetNumber("address", "street_number")
func (v *Object) GetNumber(keys ...string) (json.Number, error) {
child, err := v.getPath(keys)
if err != nil {
return "", err
} else {
n, err := child.Number()
if err != nil {
return "", err
} else {
return n, nil
}
}
return "", nil
}
// Gets the value at key path and attempts to typecast the value into a float64.
// Returns error if the value is not a json number.
// Example:
// n, err := GetNumber("address", "street_number")
func (v *Object) GetFloat64(keys ...string) (float64, error) {
child, err := v.getPath(keys)
if err != nil {
return 0, err
} else {
n, err := child.Float64()
if err != nil {
return 0, err
} else {
return n, nil
}
}
return 0, nil
}
// Gets the value at key path and attempts to typecast the value into a float64.
// Returns error if the value is not a json number.
// Example:
// n, err := GetNumber("address", "street_number")
func (v *Object) GetInt64(keys ...string) (int64, error) {
child, err := v.getPath(keys)
if err != nil {
return 0, err
} else {
n, err := child.Int64()
if err != nil {
return 0, err
} else {
return n, nil
}
}
return 0, nil
}
// Gets the value at key path and attempts to typecast the value into a bool.
// Returns error if the value is not a json boolean.
// Example:
// married, err := GetBoolean("person", "married")
func (v *Object) GetBoolean(keys ...string) (bool, error) {
child, err := v.getPath(keys)
if err != nil {
return false, err
}
return child.Boolean()
}
// Gets the value at key path and attempts to typecast the value into an array.
// Returns error if the value is not a json array.
// Consider using the more specific Get<Type>Array() since it may reduce later type casts.
// Example:
// friends, err := GetValueArray("person", "friends")
// for i, friend := range friends {
// ... // friend will be of type Value here
// }
func (v *Object) GetValueArray(keys ...string) ([]*Value, error) {
child, err := v.getPath(keys)
if err != nil {
return nil, err
} else {
return child.Array()
}
return nil, nil
}
// Gets the value at key path and attempts to typecast the value into an array of objects.
// Returns error if the value is not a json array or if any of the contained objects are not objects.
// Example:
// friends, err := GetObjectArray("person", "friends")
// for i, friend := range friends {
// ... // friend will be of type Object here
// }
func (v *Object) GetObjectArray(keys ...string) ([]*Object, error) {
child, err := v.getPath(keys)
if err != nil {
return nil, err
} else {
array, err := child.Array()
if err != nil {
return nil, err
} else {
typedArray := make([]*Object, len(array))
for index, arrayItem := range array {
typedArrayItem, err := arrayItem.
Object()
if err != nil {
return nil, err
} else {
typedArray[index] = typedArrayItem
}
}
return typedArray, nil
}
}
return nil, nil
}
// Gets the value at key path and attempts to typecast the value into an array of string.
// Returns error if the value is not a json array or if any of the contained objects are not strings.
// Gets the value at key path and attempts to typecast the value into an array of objects.
// Returns error if the value is not a json array or if any of the contained objects are not objects.
// Example:
// friendNames, err := GetStringArray("person", "friend_names")
// for i, friendName := range friendNames {
// ... // friendName will be of type string here
// }
func (v *Object) GetStringArray(keys ...string) ([]string, error) {
child, err := v.getPath(keys)
if err != nil {
return nil, err
} else {
array, err := child.Array()
if err != nil {
return nil, err
} else {
typedArray := make([]string, len(array))
for index, arrayItem := range array {
typedArrayItem, err := arrayItem.String()
if err != nil {
return nil, err
} else {
typedArray[index] = typedArrayItem
}
}
return typedArray, nil
}
}
return nil, nil
}
// Gets the value at key path and attempts to typecast the value into an array of numbers.
// Returns error if the value is not a json array or if any of the contained objects are not numbers.
// Example:
// friendAges, err := GetNumberArray("person", "friend_ages")
// for i, friendAge := range friendAges {
// ... // friendAge will be of type float64 here
// }
func (v *Object) GetNumberArray(keys ...string) ([]json.Number, error) {
child, err := v.getPath(keys)
if err != nil {
return nil, err
} else {
array, err := child.Array()
if err != nil {
return nil, err
} else {
typedArray := make([]json.Number, len(array))
for index, arrayItem := range array {
typedArrayItem, err := arrayItem.Number()
if err != nil {
return nil, err
} else {
typedArray[index] = typedArrayItem
}
}
return typedArray, nil
}
}
return nil, nil
}
// Gets the value at key path and attempts to typecast the value into an array of floats.
// Returns error if the value is not a json array or if any of the contained objects are not numbers.
func (v *Object) GetFloat64Array(keys ...string) ([]float64, error) {
child, err := v.getPath(keys)
if err != nil {
return nil, err
} else {
array, err := child.Array()
if err != nil {
return nil, err
} else {
typedArray := make([]float64, len(array))
for index, arrayItem := range array {
typedArrayItem, err := arrayItem.Float64()
if err != nil {
return nil, err
} else {
typedArray[index] = typedArrayItem
}
}
return typedArray, nil
}
}
return nil, nil
}
// Gets the value at key path and attempts to typecast the value into an array of ints.
// Returns error if the value is not a json array or if any of the contained objects are not numbers.
func (v *Object) GetInt64Array(keys ...string) ([]int64, error) {
child, err := v.getPath(keys)
if err != nil {
return nil, err
} else {
array, err := child.Array()
if err != nil {
return nil, err
} else {
typedArray := make([]int64, len(array))
for index, arrayItem := range array {
typedArrayItem, err := arrayItem.Int64()
if err != nil {
return nil, err
} else {
typedArray[index] = typedArrayItem
}
}
return typedArray, nil
}
}
return nil, nil
}
// Gets the value at key path and attempts to typecast the value into an array of bools.
// Returns error if the value is not a json array or if any of the contained objects are not booleans.
func (v *Object) GetBooleanArray(keys ...string) ([]bool, error) {
child, err := v.getPath(keys)
if err != nil {
return nil, err
} else {
array, err := child.Array()
if err != nil {
return nil, err
} else {
typedArray := make([]bool, len(array))
for index, arrayItem := range array {
typedArrayItem, err := arrayItem.Boolean()
if err != nil {
return nil, err
} else {
typedArray[index] = typedArrayItem
}
}
return typedArray, nil
}
}
return nil, nil
}
// Gets the value at key path and attempts to typecast the value into an array of nulls.
// Returns length, or an error if the value is not a json array or if any of the contained objects are not nulls.
func (v *Object) GetNullArray(keys ...string) (int64, error) {
child, err := v.getPath(keys)
if err != nil {
return 0, err
} else {
array, err := child.Array()
if err != nil {
return 0, err
} else {
var length int64 = 0
for _, arrayItem := range array {
err := arrayItem.Null()
if err != nil {
return 0, err
} else {
length++
}
}
return length, nil
}
}
return 0, nil
}
// Returns an error if the value is not actually null
func (v *Value) Null() error {
var valid bool
// Check the type of this data
switch v.data.(type) {
case nil:
valid = v.exists // Valid only if j also exists, since other values could possibly also be nil
break
}
if valid {
return nil
}
return errors.New("is not null")
}
// Attempts to typecast the current value into an array.
// Returns error if the current value is not a json array.
// Example:
// friendsArray, err := friendsValue.Array()
func (v *Value) Array() ([]*Value, error) {
var valid bool
// Check the type of this data
switch v.data.(type) {
case []interface{}:
valid = true
break
}
// Unsure if this is a good way to use slices, it's probably not
var slice []*Value
if valid {
for _, element := range v.data.([]interface{}) {
child := Value{element, true}
slice = append(slice, &child)
}
return slice, nil
}
return slice, errors.New("Not an array")
}
// Attempts to typecast the current value into a number.
// Returns error if the current value is not a json number.
// Example:
// ageNumber, err := ageValue.Number()
func (v *Value) Number() (json.Number, error) {
var valid bool
// Check the type of this data
switch v.data.(type) {
case json.Number:
valid = true
break
}
if valid {
return v.data.(json.Number), nil
}
return "", errors.New("not a number")
}
// Attempts to typecast the current value into a float64.
// Returns error if the current value is not a json number.
// Example:
// percentage, err := v.Float64()
func (v *Value) Float64() (float64, error) {
n, err := v.Number()
if err != nil {
return 0, err
}
return n.Float64()
}
// Attempts to typecast the current value into a int64.
// Returns error if the current value is not a json number.
// Example:
// id, err := v.Int64()
func (v *Value) Int64() (int64, error) {
n, err := v.Number()
if err != nil {
return 0, err
}
return n.Int64()
}
// Attempts to typecast the current value into a bool.
// Returns error if the current value is not a json boolean.
// Example:
// marriedBool, err := marriedValue.Boolean()
func (v *Value) Boolean() (bool, error) {
var valid bool
// Check the type of this data
switch v.data.(type) {
case bool:
valid = true
break
}
if valid {
return v.data.(bool), nil
}
return false, errors.New("no bool")
}
// Attempts to typecast the current value into an object.
// Returns error if the current value is not a json object.
// Example:
// friendObject, err := friendValue.Object()
func (v *Value) Object() (*Object, error) {
var valid bool
// Check the type of this data
switch v.data.(type) {
case map[string]interface{}:
valid = true
break
}
if valid {
obj := new(Object)
obj.valid = valid
m := make(map[string]*Value)
if valid {
for key, element := range v.data.(map[string]interface{}) {
m[key] = &Value{element, true}
}
}
obj.data = v.data
obj.m = m
return obj, nil
}
return nil, errors.New("not an object")
}
// Attempts to typecast the current value into a string.
// Returns error if the current value is not a json string
// Example:
// nameObject, err := nameValue.String()
func (v *Value) String() (string, error) {
var valid bool
// Check the type of this data
switch v.data.(type) {
case string:
valid = true
break
}
if valid {
return v.data.(string), nil
}
return "", errors.New("not a string")
}
// Returns the value a json formatted string.
// Note: The method named String() is used by golang's log method for logging.
// Example:
func (v *Object) String() string {
f, err := json.Marshal(v.data)
if err != nil {
return err.Error()
}
return string(f)
}