|
@@ -0,0 +1,340 @@
|
|
|
+
|
|
|
+Copyright (c) 2012, Jason Moiron
|
|
|
+
|
|
|
+ Permission is hereby granted, free of charge, to any person
|
|
|
+ obtaining a copy of this software and associated documentation
|
|
|
+ files (the "Software"), to deal in the Software without
|
|
|
+ restriction, including without limitation the rights to use,
|
|
|
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
+ copies of the Software, and to permit persons to whom the
|
|
|
+ Software is furnished to do so, subject to the following
|
|
|
+ conditions:
|
|
|
+
|
|
|
+ The above copyright notice and this permission notice shall be
|
|
|
+ included in all copies or substantial portions of the Software.
|
|
|
+
|
|
|
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
|
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
|
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
|
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
|
+ OTHER DEALINGS IN THE SOFTWARE.
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+package util
|
|
|
+
|
|
|
+import (
|
|
|
+ "bytes"
|
|
|
+ "encoding/json"
|
|
|
+ "fmt"
|
|
|
+ "strconv"
|
|
|
+)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+type JSONQuery struct {
|
|
|
+ data map[string]interface{}
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func NewJSONQuery(data []byte) (jq *JSONQuery, err error) {
|
|
|
+ obj := make(map[string]interface{})
|
|
|
+
|
|
|
+ dec := json.NewDecoder(bytes.NewReader(data))
|
|
|
+ err = dec.Decode(&obj)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ jq = new(JSONQuery)
|
|
|
+ jq.data = obj
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (jq *JSONQuery) Bool(s ...string) (bool, error) {
|
|
|
+ val, err := rquery(jq.data, s...)
|
|
|
+ if err != nil {
|
|
|
+ return false, err
|
|
|
+ }
|
|
|
+ return boolFromInterface(val)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (jq *JSONQuery) Float(s ...string) (float64, error) {
|
|
|
+ val, err := rquery(jq.data, s...)
|
|
|
+ if err != nil {
|
|
|
+ return 0.0, err
|
|
|
+ }
|
|
|
+ return floatFromInterface(val)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (jq *JSONQuery) Int(s ...string) (int, error) {
|
|
|
+ val, err := rquery(jq.data, s...)
|
|
|
+ if err != nil {
|
|
|
+ return 0, err
|
|
|
+ }
|
|
|
+ return intFromInterface(val)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (jq *JSONQuery) String(s ...string) (string, error) {
|
|
|
+ val, err := rquery(jq.data, s...)
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+ return stringFromInterface(val)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (jq *JSONQuery) Object(s ...string) (map[string]interface{}, error) {
|
|
|
+ val, err := rquery(jq.data, s...)
|
|
|
+ if err != nil {
|
|
|
+ return map[string]interface{}{}, err
|
|
|
+ }
|
|
|
+ return objectFromInterface(val)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (jq *JSONQuery) Array(s ...string) ([]interface{}, error) {
|
|
|
+ val, err := rquery(jq.data, s...)
|
|
|
+ if err != nil {
|
|
|
+ return []interface{}{}, err
|
|
|
+ }
|
|
|
+ return arrayFromInterface(val)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (jq *JSONQuery) Interface(s ...string) (interface{}, error) {
|
|
|
+ val, err := rquery(jq.data, s...)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return val, nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (jq *JSONQuery) ArrayOfStrings(s ...string) ([]string, error) {
|
|
|
+ array, err := jq.Array(s...)
|
|
|
+ if err != nil {
|
|
|
+ return []string{}, err
|
|
|
+ }
|
|
|
+ toReturn := make([]string, len(array))
|
|
|
+ for index, val := range array {
|
|
|
+ toReturn[index], err = stringFromInterface(val)
|
|
|
+ if err != nil {
|
|
|
+ return toReturn, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return toReturn, nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (jq *JSONQuery) ArrayOfInts(s ...string) ([]int, error) {
|
|
|
+ array, err := jq.Array(s...)
|
|
|
+ if err != nil {
|
|
|
+ return []int{}, err
|
|
|
+ }
|
|
|
+ toReturn := make([]int, len(array))
|
|
|
+ for index, val := range array {
|
|
|
+ toReturn[index], err = intFromInterface(val)
|
|
|
+ if err != nil {
|
|
|
+ return toReturn, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return toReturn, nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (jq *JSONQuery) ArrayOfFloats(s ...string) ([]float64, error) {
|
|
|
+ array, err := jq.Array(s...)
|
|
|
+ if err != nil {
|
|
|
+ return []float64{}, err
|
|
|
+ }
|
|
|
+ toReturn := make([]float64, len(array))
|
|
|
+ for index, val := range array {
|
|
|
+ toReturn[index], err = floatFromInterface(val)
|
|
|
+ if err != nil {
|
|
|
+ return toReturn, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return toReturn, nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (jq *JSONQuery) ArrayOfBools(s ...string) ([]bool, error) {
|
|
|
+ array, err := jq.Array(s...)
|
|
|
+ if err != nil {
|
|
|
+ return []bool{}, err
|
|
|
+ }
|
|
|
+ toReturn := make([]bool, len(array))
|
|
|
+ for index, val := range array {
|
|
|
+ toReturn[index], err = boolFromInterface(val)
|
|
|
+ if err != nil {
|
|
|
+ return toReturn, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return toReturn, nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (jq *JSONQuery) ArrayOfObjects(s ...string) ([]map[string]interface{}, error) {
|
|
|
+ array, err := jq.Array(s...)
|
|
|
+ if err != nil {
|
|
|
+ return []map[string]interface{}{}, err
|
|
|
+ }
|
|
|
+ toReturn := make([]map[string]interface{}, len(array))
|
|
|
+ for index, val := range array {
|
|
|
+ toReturn[index], err = objectFromInterface(val)
|
|
|
+ if err != nil {
|
|
|
+ return toReturn, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return toReturn, nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (jq *JSONQuery) ArrayOfArrays(s ...string) ([][]interface{}, error) {
|
|
|
+ array, err := jq.Array(s...)
|
|
|
+ if err != nil {
|
|
|
+ return [][]interface{}{}, err
|
|
|
+ }
|
|
|
+ toReturn := make([][]interface{}, len(array))
|
|
|
+ for index, val := range array {
|
|
|
+ toReturn[index], err = arrayFromInterface(val)
|
|
|
+ if err != nil {
|
|
|
+ return toReturn, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return toReturn, nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (jq *JSONQuery) Matrix2D(s ...string) ([][]interface{}, error) {
|
|
|
+ return jq.ArrayOfArrays(s...)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func rquery(blob interface{}, s ...string) (interface{}, error) {
|
|
|
+ var (
|
|
|
+ val interface{}
|
|
|
+ err error
|
|
|
+ )
|
|
|
+ val = blob
|
|
|
+ for _, q := range s {
|
|
|
+ val, err = query(val, q)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ switch val.(type) {
|
|
|
+ case nil:
|
|
|
+ return nil, fmt.Errorf("nil value")
|
|
|
+ }
|
|
|
+ return val, nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func query(blob interface{}, query string) (interface{}, error) {
|
|
|
+ index, err := strconv.Atoi(query)
|
|
|
+
|
|
|
+ if err == nil {
|
|
|
+ switch blob.(type) {
|
|
|
+ case []interface{}:
|
|
|
+ default:
|
|
|
+ return nil, fmt.Errorf("Array index on non-array")
|
|
|
+ }
|
|
|
+ if len(blob.([]interface{})) > index {
|
|
|
+ return blob.([]interface{})[index], nil
|
|
|
+ }
|
|
|
+ return nil, fmt.Errorf("Array index %d out of bounds", index)
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ switch blob.(type) {
|
|
|
+ case map[string]interface{}:
|
|
|
+ default:
|
|
|
+ return nil, fmt.Errorf("Object lookup \"%s\" on non-object", query)
|
|
|
+ }
|
|
|
+
|
|
|
+ val, ok := blob.(map[string]interface{})[query]
|
|
|
+ if !ok {
|
|
|
+ return nil, fmt.Errorf("Object does not contain field %s", query)
|
|
|
+ }
|
|
|
+ return val, nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func stringFromInterface(val interface{}) (string, error) {
|
|
|
+ switch val.(type) {
|
|
|
+ case string:
|
|
|
+ return val.(string), nil
|
|
|
+ }
|
|
|
+ return "", fmt.Errorf("Expected string value for String, got \"%v\"", val)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func boolFromInterface(val interface{}) (bool, error) {
|
|
|
+ switch val.(type) {
|
|
|
+ case bool:
|
|
|
+ return val.(bool), nil
|
|
|
+ }
|
|
|
+ return false, fmt.Errorf("Expected boolean value for Bool, got \"%v\"", val)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func floatFromInterface(val interface{}) (float64, error) {
|
|
|
+ switch val.(type) {
|
|
|
+ case float64:
|
|
|
+ return val.(float64), nil
|
|
|
+ case int:
|
|
|
+ return float64(val.(int)), nil
|
|
|
+ case string:
|
|
|
+ fval, err := strconv.ParseFloat(val.(string), 64)
|
|
|
+ if err == nil {
|
|
|
+ return fval, nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0.0, fmt.Errorf("Expected numeric value for Float, got \"%v\"", val)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func intFromInterface(val interface{}) (int, error) {
|
|
|
+ switch val.(type) {
|
|
|
+ case float64:
|
|
|
+ return int(val.(float64)), nil
|
|
|
+ case string:
|
|
|
+ ival, err := strconv.ParseFloat(val.(string), 64)
|
|
|
+ if err == nil {
|
|
|
+ return int(ival), nil
|
|
|
+ }
|
|
|
+ case int:
|
|
|
+ return val.(int), nil
|
|
|
+ }
|
|
|
+ return 0, fmt.Errorf("Expected numeric value for Int, got \"%v\"", val)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func objectFromInterface(val interface{}) (map[string]interface{}, error) {
|
|
|
+ switch val.(type) {
|
|
|
+ case map[string]interface{}:
|
|
|
+ return val.(map[string]interface{}), nil
|
|
|
+ }
|
|
|
+ return map[string]interface{}{}, fmt.Errorf("Expected json object for Object, got \"%v\"", val)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func arrayFromInterface(val interface{}) ([]interface{}, error) {
|
|
|
+ switch val.(type) {
|
|
|
+ case []interface{}:
|
|
|
+ return val.([]interface{}), nil
|
|
|
+ }
|
|
|
+ return []interface{}{}, fmt.Errorf("Expected json array for Array, got \"%v\"", val)
|
|
|
+}
|