diff --git a/simplejson.go b/simplejson.go index 516fe76..584b656 100644 --- a/simplejson.go +++ b/simplejson.go @@ -2,7 +2,6 @@ package simplejson import ( "encoding/json" - "errors" "log" ) @@ -11,14 +10,14 @@ func Version() string { return "0.5.0-alpha" } -type Json struct { +type JSON struct { data interface{} } -// NewJson returns a pointer to a new `Json` object +// NewJson returns a pointer to a new `JSON` object // after unmarshaling `body` bytes -func NewJson(body []byte) (*Json, error) { - j := new(Json) +func NewJSON(body []byte) (*JSON, error) { + j := new(JSON) err := j.UnmarshalJSON(body) if err != nil { return nil, err @@ -26,46 +25,46 @@ func NewJson(body []byte) (*Json, error) { return j, nil } -// New returns a pointer to a new, empty `Json` object -func New() *Json { - return &Json{ +// New returns a pointer to a new, empty `JSON` object +func New() *JSON { + return &JSON{ data: make(map[string]interface{}), } } // Interface returns the underlying data -func (j *Json) Interface() interface{} { +func (j *JSON) Interface() interface{} { return j.data } // Encode returns its marshaled data as `[]byte` -func (j *Json) Encode() ([]byte, error) { +func (j *JSON) Encode() ([]byte, error) { return j.MarshalJSON() } // EncodePretty returns its marshaled data as `[]byte` with indentation -func (j *Json) EncodePretty() ([]byte, error) { +func (j *JSON) EncodePretty() ([]byte, error) { return json.MarshalIndent(&j.data, "", " ") } // Implements the json.Marshaler interface. -func (j *Json) MarshalJSON() ([]byte, error) { +func (j *JSON) MarshalJSON() ([]byte, error) { return json.Marshal(&j.data) } -// Set modifies `Json` map by `key` and `value` -// Useful for changing single key/value in a `Json` object easily. -func (j *Json) Set(key string, val interface{}) { - m, err := j.Map() - if err != nil { +// Set modifies `JSON` map by `key` and `value` +// Useful for changing single key/value in a `JSON` object easily. +func (j *JSON) Set(key string, val interface{}) { + m, ok := j.CheckMap() + if !ok { return } m[key] = val } -// SetPath modifies `Json`, recursively checking/creating map keys for the supplied path, +// SetPath modifies `JSON`, recursively checking/creating map keys for the supplied path, // and then finally writing in the value -func (j *Json) SetPath(branch []string, val interface{}) { +func (j *JSON) SetPath(branch []string, val interface{}) { if len(branch) == 0 { j.data = val return @@ -102,143 +101,183 @@ func (j *Json) SetPath(branch []string, val interface{}) { curr[branch[len(branch)-1]] = val } -// Del modifies `Json` map by deleting `key` if it is present. -func (j *Json) Del(key string) { - m, err := j.Map() - if err != nil { +// Del modifies `JSON` map by deleting `key` if it is present. +func (j *JSON) Del(key string) { + m, ok := j.CheckMap() + if !ok { return } delete(m, key) } -// Get returns a pointer to a new `Json` object +// getKey returns a pointer to a new `JSON` object // for `key` in its `map` representation -// -// useful for chaining operations (to traverse a nested JSON): -// js.Get("top_level").Get("dict").Get("value").Int() -func (j *Json) Get(key string) *Json { - m, err := j.Map() - if err == nil { +// and a bool identifying success or failure +func (j *JSON) getKey(key string) (*JSON, bool) { + m, ok := j.CheckMap() + if ok { if val, ok := m[key]; ok { - return &Json{val} + return &JSON{val}, true } } - return &Json{nil} + return nil, false } -// GetPath searches for the item as specified by the branch -// without the need to deep dive using Get()'s. -// -// js.GetPath("top_level", "dict") -func (j *Json) GetPath(branch ...string) *Json { - jin := j - for _, p := range branch { - jin = jin.Get(p) +// getIndex returns a pointer to a new `JSON` object +// for `index` in its `array` representation +// and a bool identifying success or failure +func (j *JSON) getIndex(index int) (*JSON, bool) { + a, ok := j.CheckArray() + if ok { + if len(a) > index { + return &JSON{a[index]}, true + } } - return jin + return nil, false } -// GetIndex returns a pointer to a new `Json` object -// for `index` in its `array` representation +// Get searches for the item as specified by the branch +// within a nested JSON and returns a new JSON pointer +// the pointer is always a valid JSON, allowing for chained operations // -// this is the analog to Get when accessing elements of -// a json array instead of a json object: -// js.Get("top_level").Get("array").GetIndex(1).Get("key").Int() -func (j *Json) GetIndex(index int) *Json { - a, err := j.Array() - if err == nil { - if len(a) > index { - return &Json{a[index]} - } +// newJs := js.Get("top_level", "entries", 3, "dict") +func (j *JSON) Get(branch ...interface{}) *JSON { + jin, ok := j.CheckGet(branch...) + if ok { + return jin } - return &Json{nil} + return &JSON{nil} } -// CheckGet returns a pointer to a new `Json` object and -// a `bool` identifying success or failure +// CheckGet is like Get, except it also returns a bool +// indicating whenever the branch was found or not +// the JSON pointer mai be nil // -// useful for chained operations when success is important: -// if data, ok := js.Get("top_level").CheckGet("inner"); ok { -// log.Println(data) -// } -func (j *Json) CheckGet(key string) (*Json, bool) { - m, err := j.Map() - if err == nil { - if val, ok := m[key]; ok { - return &Json{val}, true +// newJs, ok := js.Get("top_level", "entries", 3, "dict") +func (j *JSON) CheckGet(branch ...interface{}) (*JSON, bool) { + jin := j + var ok bool + for _, p := range branch { + switch p.(type) { + case string: + jin, ok = jin.getKey(p.(string)) + case int: + jin, ok = jin.getIndex(p.(int)) + default: + ok = false + } + if !ok { + return nil, false } } - return nil, false + return jin, true +} + +// CheckJSONMap returns a copy of a JSON map, but with values as Jsons +func (j *JSON) CheckJSONMap() (map[string]*JSON, bool) { + m, ok := j.CheckMap() + if !ok { + return nil, false + } + jm := make(map[string]*JSON) + for key, val := range m { + jm[key] = &JSON{val} + } + return jm, true } -// Map type asserts to `map` -func (j *Json) Map() (map[string]interface{}, error) { +// CheckJSONArray returns a copy of an array, but with each value as a JSON +func (j *JSON) CheckJSONArray() ([]*JSON, bool) { + a, ok := j.CheckArray() + if !ok { + return nil, false + } + ja := make([]*JSON, len(a)) + for key, val := range a { + ja[key] = &JSON{val} + } + return ja, true +} + +// CheckMap type asserts to `map` +func (j *JSON) CheckMap() (map[string]interface{}, bool) { if m, ok := (j.data).(map[string]interface{}); ok { - return m, nil + return m, true } - return nil, errors.New("type assertion to map[string]interface{} failed") + return nil, false } -// Array type asserts to an `array` -func (j *Json) Array() ([]interface{}, error) { +// CheckArray type asserts to an `array` +func (j *JSON) CheckArray() ([]interface{}, bool) { if a, ok := (j.data).([]interface{}); ok { - return a, nil + return a, true } - return nil, errors.New("type assertion to []interface{} failed") + return nil, false } -// Bool type asserts to `bool` -func (j *Json) Bool() (bool, error) { +// CheckBool type asserts to `bool` +func (j *JSON) CheckBool() (bool, bool) { if s, ok := (j.data).(bool); ok { - return s, nil + return s, true } - return false, errors.New("type assertion to bool failed") + return false, false } -// String type asserts to `string` -func (j *Json) String() (string, error) { +// CheckString type asserts to `string` +func (j *JSON) CheckString() (string, bool) { if s, ok := (j.data).(string); ok { - return s, nil + return s, true } - return "", errors.New("type assertion to string failed") + return "", false } -// Bytes type asserts to `[]byte` -func (j *Json) Bytes() ([]byte, error) { - if s, ok := (j.data).(string); ok { - return []byte(s), nil +// JSONArray guarantees the return of a `[]*JSON` (with optional default) +func (j *JSON) JSONArray(args ...[]*JSON) []*JSON { + var def []*JSON + + switch len(args) { + case 0: + case 1: + def = args[0] + default: + log.Panicf("JSONArray() received too many arguments %d", len(args)) } - return nil, errors.New("type assertion to []byte failed") + + a, ok := j.CheckJSONArray() + if ok { + return a + } + + return def } -// StringArray type asserts to an `array` of `string` -func (j *Json) StringArray() ([]string, error) { - arr, err := j.Array() - if err != nil { - return nil, err +// JSONMap guarantees the return of a `map[string]*JSON` (with optional default) +func (j *JSON) JSONMap(args ...map[string]*JSON) map[string]*JSON { + var def map[string]*JSON + + switch len(args) { + case 0: + case 1: + def = args[0] + default: + log.Panicf("JSONMap() received too many arguments %d", len(args)) } - retArr := make([]string, 0, len(arr)) - for _, a := range arr { - if a == nil { - retArr = append(retArr, "") - continue - } - s, ok := a.(string) - if !ok { - return nil, err - } - retArr = append(retArr, s) + + a, ok := j.CheckJSONMap() + if ok { + return a } - return retArr, nil + + return def } -// MustArray guarantees the return of a `[]interface{}` (with optional default) +// Array guarantees the return of a `[]interface{}` (with optional default) // // useful when you want to interate over array values in a succinct manner: -// for i, v := range js.Get("results").MustArray() { +// for i, v := range js.Get("results").Array() { // fmt.Println(i, v) // } -func (j *Json) MustArray(args ...[]interface{}) []interface{} { +func (j *JSON) Array(args ...[]interface{}) []interface{} { var def []interface{} switch len(args) { @@ -246,24 +285,24 @@ func (j *Json) MustArray(args ...[]interface{}) []interface{} { case 1: def = args[0] default: - log.Panicf("MustArray() received too many arguments %d", len(args)) + log.Panicf("Array() received too many arguments %d", len(args)) } - a, err := j.Array() - if err == nil { + a, ok := j.CheckArray() + if ok { return a } return def } -// MustMap guarantees the return of a `map[string]interface{}` (with optional default) +// Map guarantees the return of a `map[string]interface{}` (with optional default) // // useful when you want to interate over map values in a succinct manner: -// for k, v := range js.Get("dictionary").MustMap() { +// for k, v := range js.Get("dictionary").Map() { // fmt.Println(k, v) // } -func (j *Json) MustMap(args ...map[string]interface{}) map[string]interface{} { +func (j *JSON) Map(args ...map[string]interface{}) map[string]interface{} { var def map[string]interface{} switch len(args) { @@ -271,22 +310,22 @@ func (j *Json) MustMap(args ...map[string]interface{}) map[string]interface{} { case 1: def = args[0] default: - log.Panicf("MustMap() received too many arguments %d", len(args)) + log.Panicf("Map() received too many arguments %d", len(args)) } - a, err := j.Map() - if err == nil { + a, ok := j.CheckMap() + if ok { return a } return def } -// MustString guarantees the return of a `string` (with optional default) +// String guarantees the return of a `string` (with optional default) // // useful when you explicitly want a `string` in a single value return context: -// myFunc(js.Get("param1").MustString(), js.Get("optional_param").MustString("my_default")) -func (j *Json) MustString(args ...string) string { +// myFunc(js.Get("param1").String(), js.Get("optional_param").String("my_default")) +func (j *JSON) String(args ...string) string { var def string switch len(args) { @@ -294,22 +333,22 @@ func (j *Json) MustString(args ...string) string { case 1: def = args[0] default: - log.Panicf("MustString() received too many arguments %d", len(args)) + log.Panicf("String() received too many arguments %d", len(args)) } - s, err := j.String() - if err == nil { + s, ok := j.CheckString() + if ok { return s } return def } -// MustInt guarantees the return of an `int` (with optional default) +// Int guarantees the return of an `int` (with optional default) // // useful when you explicitly want an `int` in a single value return context: -// myFunc(js.Get("param1").MustInt(), js.Get("optional_param").MustInt(5150)) -func (j *Json) MustInt(args ...int) int { +// myFunc(js.Get("param1").Int(), js.Get("optional_param").Int(5150)) +func (j *JSON) Int(args ...int) int { var def int switch len(args) { @@ -317,22 +356,22 @@ func (j *Json) MustInt(args ...int) int { case 1: def = args[0] default: - log.Panicf("MustInt() received too many arguments %d", len(args)) + log.Panicf("Int() received too many arguments %d", len(args)) } - i, err := j.Int() - if err == nil { + i, ok := j.CheckInt() + if ok { return i } return def } -// MustFloat64 guarantees the return of a `float64` (with optional default) +// Float64 guarantees the return of a `float64` (with optional default) // // useful when you explicitly want a `float64` in a single value return context: -// myFunc(js.Get("param1").MustFloat64(), js.Get("optional_param").MustFloat64(5.150)) -func (j *Json) MustFloat64(args ...float64) float64 { +// myFunc(js.Get("param1").Float64(), js.Get("optional_param").Float64(5.150)) +func (j *JSON) Float64(args ...float64) float64 { var def float64 switch len(args) { @@ -340,22 +379,22 @@ func (j *Json) MustFloat64(args ...float64) float64 { case 1: def = args[0] default: - log.Panicf("MustFloat64() received too many arguments %d", len(args)) + log.Panicf("Float64() received too many arguments %d", len(args)) } - f, err := j.Float64() - if err == nil { + f, ok := j.CheckFloat64() + if ok { return f } return def } -// MustBool guarantees the return of a `bool` (with optional default) +// Bool guarantees the return of a `bool` (with optional default) // // useful when you explicitly want a `bool` in a single value return context: -// myFunc(js.Get("param1").MustBool(), js.Get("optional_param").MustBool(true)) -func (j *Json) MustBool(args ...bool) bool { +// myFunc(js.Get("param1").Bool(), js.Get("optional_param").Bool(true)) +func (j *JSON) Bool(args ...bool) bool { var def bool switch len(args) { @@ -363,22 +402,22 @@ func (j *Json) MustBool(args ...bool) bool { case 1: def = args[0] default: - log.Panicf("MustBool() received too many arguments %d", len(args)) + log.Panicf("Bool() received too many arguments %d", len(args)) } - b, err := j.Bool() - if err == nil { + b, ok := j.CheckBool() + if ok { return b } return def } -// MustInt64 guarantees the return of an `int64` (with optional default) +// Int64 guarantees the return of an `int64` (with optional default) // // useful when you explicitly want an `int64` in a single value return context: -// myFunc(js.Get("param1").MustInt64(), js.Get("optional_param").MustInt64(5150)) -func (j *Json) MustInt64(args ...int64) int64 { +// myFunc(js.Get("param1").Int64(), js.Get("optional_param").Int64(5150)) +func (j *JSON) Int64(args ...int64) int64 { var def int64 switch len(args) { @@ -386,22 +425,22 @@ func (j *Json) MustInt64(args ...int64) int64 { case 1: def = args[0] default: - log.Panicf("MustInt64() received too many arguments %d", len(args)) + log.Panicf("Int64() received too many arguments %d", len(args)) } - i, err := j.Int64() - if err == nil { + i, ok := j.CheckInt64() + if ok { return i } return def } -// MustUInt64 guarantees the return of an `uint64` (with optional default) +// UInt64 guarantees the return of an `uint64` (with optional default) // // useful when you explicitly want an `uint64` in a single value return context: -// myFunc(js.Get("param1").MustUint64(), js.Get("optional_param").MustUint64(5150)) -func (j *Json) MustUint64(args ...uint64) uint64 { +// myFunc(js.Get("param1").Uint64(), js.Get("optional_param").Uint64(5150)) +func (j *JSON) Uint64(args ...uint64) uint64 { var def uint64 switch len(args) { @@ -409,11 +448,11 @@ func (j *Json) MustUint64(args ...uint64) uint64 { case 1: def = args[0] default: - log.Panicf("MustUint64() received too many arguments %d", len(args)) + log.Panicf("Uint64() received too many arguments %d", len(args)) } - i, err := j.Uint64() - if err == nil { + i, ok := j.CheckUint64() + if ok { return i } diff --git a/simplejson_go10.go b/simplejson_go10.go index c9151e9..0a24bbd 100644 --- a/simplejson_go10.go +++ b/simplejson_go10.go @@ -4,72 +4,71 @@ package simplejson import ( "encoding/json" - "errors" "io" "reflect" ) -// NewFromReader returns a *Json by decoding from an io.Reader -func NewFromReader(r io.Reader) (*Json, error) { - j := new(Json) +// NewFromReader returns a *JSON by decoding from an io.Reader +func NewFromReader(r io.Reader) (*JSON, error) { + j := new(JSON) dec := json.NewDecoder(r) err := dec.Decode(&j.data) return j, err } // Implements the json.Unmarshaler interface. -func (j *Json) UnmarshalJSON(p []byte) error { +func (j *JSON) UnmarshalJSON(p []byte) error { return json.Unmarshal(p, &j.data) } -// Float64 coerces into a float64 -func (j *Json) Float64() (float64, error) { +// CheckFloat64 coerces into a float64 +func (j *JSON) CheckFloat64() (float64, bool) { switch j.data.(type) { case float32, float64: - return reflect.ValueOf(j.data).Float(), nil + return reflect.ValueOf(j.data).Float(), true case int, int8, int16, int32, int64: - return float64(reflect.ValueOf(j.data).Int()), nil + return float64(reflect.ValueOf(j.data).Int()), true case uint, uint8, uint16, uint32, uint64: - return float64(reflect.ValueOf(j.data).Uint()), nil + return float64(reflect.ValueOf(j.data).Uint()), true } - return 0, errors.New("invalid value type") + return 0, false } -// Int coerces into an int -func (j *Json) Int() (int, error) { +// CheckInt coerces into an int +func (j *JSON) CheckInt() (int, bool) { switch j.data.(type) { case float32, float64: - return int(reflect.ValueOf(j.data).Float()), nil + return int(reflect.ValueOf(j.data).Float()), true case int, int8, int16, int32, int64: - return int(reflect.ValueOf(j.data).Int()), nil + return int(reflect.ValueOf(j.data).Int()), true case uint, uint8, uint16, uint32, uint64: - return int(reflect.ValueOf(j.data).Uint()), nil + return int(reflect.ValueOf(j.data).Uint()), true } - return 0, errors.New("invalid value type") + return 0, false } -// Int64 coerces into an int64 -func (j *Json) Int64() (int64, error) { +// CheckInt64 coerces into an int64 +func (j *JSON) CheckInt64() (int64, bool) { switch j.data.(type) { case float32, float64: - return int64(reflect.ValueOf(j.data).Float()), nil + return int64(reflect.ValueOf(j.data).Float()), true case int, int8, int16, int32, int64: - return reflect.ValueOf(j.data).Int(), nil + return reflect.ValueOf(j.data).Int(), true case uint, uint8, uint16, uint32, uint64: - return int64(reflect.ValueOf(j.data).Uint()), nil + return int64(reflect.ValueOf(j.data).Uint()), true } - return 0, errors.New("invalid value type") + return 0, false } -// Uint64 coerces into an uint64 -func (j *Json) Uint64() (uint64, error) { +// CheckUint64 coerces into an uint64 +func (j *JSON) CheckUint64() (uint64, bool) { switch j.data.(type) { case float32, float64: - return uint64(reflect.ValueOf(j.data).Float()), nil + return uint64(reflect.ValueOf(j.data).Float()), true case int, int8, int16, int32, int64: - return uint64(reflect.ValueOf(j.data).Int()), nil + return uint64(reflect.ValueOf(j.data).Int()), true case uint, uint8, uint16, uint32, uint64: - return reflect.ValueOf(j.data).Uint(), nil + return reflect.ValueOf(j.data).Uint(), true } - return 0, errors.New("invalid value type") + return 0, false } diff --git a/simplejson_go10_test.go b/simplejson_go10_test.go index 4aa6a51..1d7b1da 100644 --- a/simplejson_go10_test.go +++ b/simplejson_go10_test.go @@ -26,7 +26,7 @@ func TestNewFromReader(t *testing.T) { assert.NotEqual(t, nil, js) assert.Equal(t, nil, err) - arr, _ := js.Get("test").Get("array").Array() + arr, _ := js.Get("test").Get("array").CheckArray() assert.NotEqual(t, nil, arr) for i, v := range arr { var iv int @@ -39,17 +39,17 @@ func TestNewFromReader(t *testing.T) { assert.Equal(t, i+1, iv) } - ma := js.Get("test").Get("array").MustArray() + ma := js.Get("test").Get("array").Array() assert.Equal(t, ma, []interface{}{float64(1), "2", float64(3)}) - mm := js.Get("test").Get("arraywithsubs").GetIndex(0).MustMap() + mm := js.Get("test").Get("arraywithsubs").Get(0).Map() assert.Equal(t, mm, map[string]interface{}{"subkeyone": float64(1)}) - assert.Equal(t, js.Get("test").Get("bignum").MustInt64(), int64(8000000000)) + assert.Equal(t, js.Get("test").Get("bignum").Int64(), int64(8000000000)) } func TestSimplejsonGo10(t *testing.T) { - js, err := NewJson([]byte(`{ + js, err := NewJSON([]byte(`{ "test": { "array": [1, "2", 3], "arraywithsubs": [ @@ -63,7 +63,7 @@ func TestSimplejsonGo10(t *testing.T) { assert.NotEqual(t, nil, js) assert.Equal(t, nil, err) - arr, _ := js.Get("test").Get("array").Array() + arr, _ := js.Get("test").Get("array").CheckArray() assert.NotEqual(t, nil, arr) for i, v := range arr { var iv int @@ -76,11 +76,11 @@ func TestSimplejsonGo10(t *testing.T) { assert.Equal(t, i+1, iv) } - ma := js.Get("test").Get("array").MustArray() + ma := js.Get("test").Get("array").Array() assert.Equal(t, ma, []interface{}{float64(1), "2", float64(3)}) - mm := js.Get("test").Get("arraywithsubs").GetIndex(0).MustMap() + mm := js.Get("test").Get("arraywithsubs").Get(0).Map() assert.Equal(t, mm, map[string]interface{}{"subkeyone": float64(1)}) - assert.Equal(t, js.Get("test").Get("bignum").MustInt64(), int64(8000000000)) + assert.Equal(t, js.Get("test").Get("bignum").Int64(), int64(8000000000)) } diff --git a/simplejson_go11.go b/simplejson_go11.go index 1c47953..6498790 100644 --- a/simplejson_go11.go +++ b/simplejson_go11.go @@ -5,85 +5,87 @@ package simplejson import ( "bytes" "encoding/json" - "errors" "io" "reflect" "strconv" ) // Implements the json.Unmarshaler interface. -func (j *Json) UnmarshalJSON(p []byte) error { +func (j *JSON) UnmarshalJSON(p []byte) error { dec := json.NewDecoder(bytes.NewBuffer(p)) dec.UseNumber() return dec.Decode(&j.data) } -// NewFromReader returns a *Json by decoding from an io.Reader -func NewFromReader(r io.Reader) (*Json, error) { - j := new(Json) +// NewFromReader returns a *JSON by decoding from an io.Reader +func NewFromReader(r io.Reader) (*JSON, error) { + j := new(JSON) dec := json.NewDecoder(r) dec.UseNumber() err := dec.Decode(&j.data) return j, err } -// Float64 coerces into a float64 -func (j *Json) Float64() (float64, error) { +// CheckFloat64 coerces into a float64 +func (j *JSON) CheckFloat64() (float64, bool) { switch j.data.(type) { case json.Number: - return j.data.(json.Number).Float64() + nr, err := j.data.(json.Number).Float64() + return nr, err == nil case float32, float64: - return reflect.ValueOf(j.data).Float(), nil + return reflect.ValueOf(j.data).Float(), true case int, int8, int16, int32, int64: - return float64(reflect.ValueOf(j.data).Int()), nil + return float64(reflect.ValueOf(j.data).Int()), true case uint, uint8, uint16, uint32, uint64: - return float64(reflect.ValueOf(j.data).Uint()), nil + return float64(reflect.ValueOf(j.data).Uint()), true } - return 0, errors.New("invalid value type") + return 0, false } -// Int coerces into an int -func (j *Json) Int() (int, error) { +// CheckInt coerces into an int +func (j *JSON) CheckInt() (int, bool) { switch j.data.(type) { case json.Number: - i, err := j.data.(json.Number).Int64() - return int(i), err + nr, err := j.data.(json.Number).Int64() + return int(nr), err == nil case float32, float64: - return int(reflect.ValueOf(j.data).Float()), nil + return int(reflect.ValueOf(j.data).Float()), true case int, int8, int16, int32, int64: - return int(reflect.ValueOf(j.data).Int()), nil + return int(reflect.ValueOf(j.data).Int()), true case uint, uint8, uint16, uint32, uint64: - return int(reflect.ValueOf(j.data).Uint()), nil + return int(reflect.ValueOf(j.data).Uint()), true } - return 0, errors.New("invalid value type") + return 0, false } -// Int64 coerces into an int64 -func (j *Json) Int64() (int64, error) { +// CheckInt64 coerces into an int64 +func (j *JSON) CheckInt64() (int64, bool) { switch j.data.(type) { case json.Number: - return j.data.(json.Number).Int64() + nr, err := j.data.(json.Number).Int64() + return nr, err == nil case float32, float64: - return int64(reflect.ValueOf(j.data).Float()), nil + return int64(reflect.ValueOf(j.data).Float()), true case int, int8, int16, int32, int64: - return reflect.ValueOf(j.data).Int(), nil + return reflect.ValueOf(j.data).Int(), true case uint, uint8, uint16, uint32, uint64: - return int64(reflect.ValueOf(j.data).Uint()), nil + return int64(reflect.ValueOf(j.data).Uint()), true } - return 0, errors.New("invalid value type") + return 0, false } -// Uint64 coerces into an uint64 -func (j *Json) Uint64() (uint64, error) { +// CheckUint64 coerces into an uint64 +func (j *JSON) CheckUint64() (uint64, bool) { switch j.data.(type) { case json.Number: - return strconv.ParseUint(j.data.(json.Number).String(), 10, 64) + nr, err := strconv.ParseUint(j.data.(json.Number).String(), 10, 64) + return nr, err == nil case float32, float64: - return uint64(reflect.ValueOf(j.data).Float()), nil + return uint64(reflect.ValueOf(j.data).Float()), true case int, int8, int16, int32, int64: - return uint64(reflect.ValueOf(j.data).Int()), nil + return uint64(reflect.ValueOf(j.data).Int()), true case uint, uint8, uint16, uint32, uint64: - return reflect.ValueOf(j.data).Uint(), nil + return reflect.ValueOf(j.data).Uint(), true } - return 0, errors.New("invalid value type") + return 0, false } diff --git a/simplejson_go11_test.go b/simplejson_go11_test.go index 16362ad..97af515 100644 --- a/simplejson_go11_test.go +++ b/simplejson_go11_test.go @@ -29,7 +29,7 @@ func TestNewFromReader(t *testing.T) { assert.NotEqual(t, nil, js) assert.Equal(t, nil, err) - arr, _ := js.Get("test").Get("array").Array() + arr, _ := js.Get("test").Get("array").CheckArray() assert.NotEqual(t, nil, arr) for i, v := range arr { var iv int @@ -44,18 +44,18 @@ func TestNewFromReader(t *testing.T) { assert.Equal(t, i+1, iv) } - ma := js.Get("test").Get("array").MustArray() + ma := js.Get("test").Get("array").Array() assert.Equal(t, ma, []interface{}{json.Number("1"), "2", json.Number("3")}) - mm := js.Get("test").Get("arraywithsubs").GetIndex(0).MustMap() + mm := js.Get("test").Get("arraywithsubs").Get(0).Map() assert.Equal(t, mm, map[string]interface{}{"subkeyone": json.Number("1")}) - assert.Equal(t, js.Get("test").Get("bignum").MustInt64(), int64(9223372036854775807)) - assert.Equal(t, js.Get("test").Get("uint64").MustUint64(), uint64(18446744073709551615)) + assert.Equal(t, js.Get("test").Get("bignum").Int64(), int64(9223372036854775807)) + assert.Equal(t, js.Get("test").Get("uint64").Uint64(), uint64(18446744073709551615)) } func TestSimplejsonGo11(t *testing.T) { - js, err := NewJson([]byte(`{ + js, err := NewJSON([]byte(`{ "test": { "array": [1, "2", 3], "arraywithsubs": [ @@ -70,7 +70,7 @@ func TestSimplejsonGo11(t *testing.T) { assert.NotEqual(t, nil, js) assert.Equal(t, nil, err) - arr, _ := js.Get("test").Get("array").Array() + arr, _ := js.Get("test").Get("array").CheckArray() assert.NotEqual(t, nil, arr) for i, v := range arr { var iv int @@ -85,12 +85,12 @@ func TestSimplejsonGo11(t *testing.T) { assert.Equal(t, i+1, iv) } - ma := js.Get("test").Get("array").MustArray() + ma := js.Get("test").Get("array").Array() assert.Equal(t, ma, []interface{}{json.Number("1"), "2", json.Number("3")}) - mm := js.Get("test").Get("arraywithsubs").GetIndex(0).MustMap() + mm := js.Get("test").Get("arraywithsubs").Get(0).Map() assert.Equal(t, mm, map[string]interface{}{"subkeyone": json.Number("1")}) - assert.Equal(t, js.Get("test").Get("bignum").MustInt64(), int64(9223372036854775807)) - assert.Equal(t, js.Get("test").Get("uint64").MustUint64(), uint64(18446744073709551615)) + assert.Equal(t, js.Get("test").Get("bignum").Int64(), int64(9223372036854775807)) + assert.Equal(t, js.Get("test").Get("uint64").Uint64(), uint64(18446744073709551615)) } diff --git a/simplejson_test.go b/simplejson_test.go index 5a11156..fc16ae1 100644 --- a/simplejson_test.go +++ b/simplejson_test.go @@ -11,7 +11,7 @@ func TestSimplejson(t *testing.T) { var ok bool var err error - js, err := NewJson([]byte(`{ + js, err := NewJSON([]byte(`{ "test": { "string_array": ["asdf", "ghjk", "zxcv"], "string_array_null": ["abc", null, "efg"], @@ -38,87 +38,97 @@ func TestSimplejson(t *testing.T) { aws := js.Get("test").Get("arraywithsubs") assert.NotEqual(t, nil, aws) var awsval int - awsval, _ = aws.GetIndex(0).Get("subkeyone").Int() + awsval, _ = aws.Get(0).Get("subkeyone").CheckInt() assert.Equal(t, 1, awsval) - awsval, _ = aws.GetIndex(1).Get("subkeytwo").Int() + awsval, _ = aws.Get(1).Get("subkeytwo").CheckInt() assert.Equal(t, 2, awsval) - awsval, _ = aws.GetIndex(1).Get("subkeythree").Int() + awsval, _ = aws.Get(1).Get("subkeythree").CheckInt() assert.Equal(t, 3, awsval) - i, _ := js.Get("test").Get("int").Int() + i, _ := js.Get("test").Get("int").CheckInt() assert.Equal(t, 10, i) - f, _ := js.Get("test").Get("float").Float64() + f, _ := js.Get("test").Get("float").CheckFloat64() assert.Equal(t, 5.150, f) - s, _ := js.Get("test").Get("string").String() + s, _ := js.Get("test").Get("string").CheckString() assert.Equal(t, "simplejson", s) - b, _ := js.Get("test").Get("bool").Bool() + b, _ := js.Get("test").Get("bool").CheckBool() assert.Equal(t, true, b) - mi := js.Get("test").Get("int").MustInt() + mi := js.Get("test").Get("int").Int() assert.Equal(t, 10, mi) - mi2 := js.Get("test").Get("missing_int").MustInt(5150) + mi2 := js.Get("test").Get("missing_int").Int(5150) assert.Equal(t, 5150, mi2) - ms := js.Get("test").Get("string").MustString() + ms := js.Get("test").Get("string").String() assert.Equal(t, "simplejson", ms) - ms2 := js.Get("test").Get("missing_string").MustString("fyea") + ms2 := js.Get("test").Get("missing_string").String("fyea") assert.Equal(t, "fyea", ms2) - ma2 := js.Get("test").Get("missing_array").MustArray([]interface{}{"1", 2, "3"}) + ma2 := js.Get("test").Get("missing_array").Array([]interface{}{"1", 2, "3"}) assert.Equal(t, ma2, []interface{}{"1", 2, "3"}) - mm2 := js.Get("test").Get("missing_map").MustMap(map[string]interface{}{"found": false}) + mm2 := js.Get("test").Get("missing_map").Map(map[string]interface{}{"found": false}) assert.Equal(t, mm2, map[string]interface{}{"found": false}) - strs, err := js.Get("test").Get("string_array").StringArray() - assert.Equal(t, err, nil) - assert.Equal(t, strs[0], "asdf") - assert.Equal(t, strs[1], "ghjk") - assert.Equal(t, strs[2], "zxcv") - - strs2, err := js.Get("test").Get("string_array_null").StringArray() - assert.Equal(t, err, nil) - assert.Equal(t, strs2[0], "abc") - assert.Equal(t, strs2[1], "") - assert.Equal(t, strs2[2], "efg") - - gp, _ := js.GetPath("test", "string").String() + gp, _ := js.Get("test", "string").CheckString() assert.Equal(t, "simplejson", gp) - gp2, _ := js.GetPath("test", "int").Int() + gp2, _ := js.Get("test", "int").CheckInt() assert.Equal(t, 10, gp2) - assert.Equal(t, js.Get("test").Get("bool").MustBool(), true) + gpa, _ := js.Get("test", "string_array", 0).CheckString() + assert.Equal(t, "asdf", gpa) + + gpa2, _ := js.Get("test", "arraywithsubs", 1, "subkeythree").CheckInt() + assert.Equal(t, 3, gpa2) + + jm, ok := js.Get("test").CheckJSONMap() + assert.Equal(t, ok, true) + jmbool, _ := jm["bool"].CheckBool() + assert.Equal(t, true, jmbool) + + ja, ok := js.Get("test", "string_array").CheckJSONArray() + assert.Equal(t, ok, true) + jastr, _ := ja[0].CheckString() + assert.Equal(t, "asdf", jastr) + + assert.Equal(t, js.Get("test").Get("bool").Bool(), true) js.Set("float2", 300.0) - assert.Equal(t, js.Get("float2").MustFloat64(), 300.0) + assert.Equal(t, js.Get("float2").Float64(), 300.0) js.Set("test2", "setTest") - assert.Equal(t, "setTest", js.Get("test2").MustString()) + assert.Equal(t, "setTest", js.Get("test2").String()) js.Del("test2") - assert.NotEqual(t, "setTest", js.Get("test2").MustString()) + assert.NotEqual(t, "setTest", js.Get("test2").String()) js.Get("test").Get("sub_obj").Set("a", 2) - assert.Equal(t, 2, js.Get("test").Get("sub_obj").Get("a").MustInt()) + assert.Equal(t, 2, js.Get("test").Get("sub_obj").Get("a").Int()) + + js.Get("test", "sub_obj").Set("a", 3) + assert.Equal(t, 3, js.Get("test", "sub_obj", "a").Int()) - js.GetPath("test", "sub_obj").Set("a", 3) - assert.Equal(t, 3, js.GetPath("test", "sub_obj", "a").MustInt()) + jmm := js.Get("missing_map").JSONMap(map[string]*JSON{"js1": js}) + assert.Equal(t, js, jmm["js1"]) + + jma := js.Get("missing_array").JSONArray([]*JSON{js}) + assert.Equal(t, js, jma[0]) } func TestStdlibInterfaces(t *testing.T) { val := new(struct { Name string `json:"name"` - Params *Json `json:"params"` + Params *JSON `json:"params"` }) val2 := new(struct { Name string `json:"name"` - Params *Json `json:"params"` + Params *JSON `json:"params"` }) raw := `{"name":"myobject","params":{"string":"simplejson"}}` @@ -127,7 +137,7 @@ func TestStdlibInterfaces(t *testing.T) { assert.Equal(t, "myobject", val.Name) assert.NotEqual(t, nil, val.Params.data) - s, _ := val.Params.Get("string").String() + s, _ := val.Params.Get("string").CheckString() assert.Equal(t, "simplejson", s) p, err := json.Marshal(val) @@ -137,99 +147,99 @@ func TestStdlibInterfaces(t *testing.T) { } func TestSet(t *testing.T) { - js, err := NewJson([]byte(`{}`)) + js, err := NewJSON([]byte(`{}`)) assert.Equal(t, nil, err) js.Set("baz", "bing") - s, err := js.GetPath("baz").String() - assert.Equal(t, nil, err) + s, ok := js.Get("baz").CheckString() + assert.Equal(t, true, ok) assert.Equal(t, "bing", s) } func TestReplace(t *testing.T) { - js, err := NewJson([]byte(`{}`)) + js, err := NewJSON([]byte(`{}`)) assert.Equal(t, nil, err) err = js.UnmarshalJSON([]byte(`{"baz":"bing"}`)) assert.Equal(t, nil, err) - s, err := js.GetPath("baz").String() - assert.Equal(t, nil, err) + s, ok := js.Get("baz").CheckString() + assert.Equal(t, true, ok) assert.Equal(t, "bing", s) } func TestSetPath(t *testing.T) { - js, err := NewJson([]byte(`{}`)) + js, err := NewJSON([]byte(`{}`)) assert.Equal(t, nil, err) js.SetPath([]string{"foo", "bar"}, "baz") - s, err := js.GetPath("foo", "bar").String() - assert.Equal(t, nil, err) + s, ok := js.Get("foo", "bar").CheckString() + assert.Equal(t, true, ok) assert.Equal(t, "baz", s) } func TestSetPathNoPath(t *testing.T) { - js, err := NewJson([]byte(`{"some":"data","some_number":1.0,"some_bool":false}`)) + js, err := NewJSON([]byte(`{"some":"data","some_number":1.0,"some_bool":false}`)) assert.Equal(t, nil, err) - f := js.GetPath("some_number").MustFloat64(99.0) + f := js.Get("some_number").Float64(99.0) assert.Equal(t, f, 1.0) js.SetPath([]string{}, map[string]interface{}{"foo": "bar"}) - s, err := js.GetPath("foo").String() - assert.Equal(t, nil, err) + s, ok := js.Get("foo").CheckString() + assert.Equal(t, true, ok) assert.Equal(t, "bar", s) - f = js.GetPath("some_number").MustFloat64(99.0) + f = js.Get("some_number").Float64(99.0) assert.Equal(t, f, 99.0) } func TestPathWillAugmentExisting(t *testing.T) { - js, err := NewJson([]byte(`{"this":{"a":"aa","b":"bb","c":"cc"}}`)) + js, err := NewJSON([]byte(`{"this":{"a":"aa","b":"bb","c":"cc"}}`)) assert.Equal(t, nil, err) js.SetPath([]string{"this", "d"}, "dd") cases := []struct { - path []string + path []interface{} outcome string }{ { - path: []string{"this", "a"}, + path: []interface{}{"this", "a"}, outcome: "aa", }, { - path: []string{"this", "b"}, + path: []interface{}{"this", "b"}, outcome: "bb", }, { - path: []string{"this", "c"}, + path: []interface{}{"this", "c"}, outcome: "cc", }, { - path: []string{"this", "d"}, + path: []interface{}{"this", "d"}, outcome: "dd", }, } for _, tc := range cases { - s, err := js.GetPath(tc.path...).String() - assert.Equal(t, nil, err) + s, ok := js.Get(tc.path...).CheckString() + assert.Equal(t, true, ok) assert.Equal(t, tc.outcome, s) } } func TestPathWillOverwriteExisting(t *testing.T) { // notice how "a" is 0.1 - but then we'll try to set at path a, foo - js, err := NewJson([]byte(`{"this":{"a":0.1,"b":"bb","c":"cc"}}`)) + js, err := NewJSON([]byte(`{"this":{"a":0.1,"b":"bb","c":"cc"}}`)) assert.Equal(t, nil, err) js.SetPath([]string{"this", "a", "foo"}, "bar") - s, err := js.GetPath("this", "a", "foo").String() - assert.Equal(t, nil, err) + s, ok := js.Get("this", "a", "foo").CheckString() + assert.Equal(t, true, ok) assert.Equal(t, "bar", s) }