diff options
author | Rastislav Szabo <raszabo@cisco.com> | 2017-05-04 11:09:03 +0200 |
---|---|---|
committer | Rastislav Szabo <raszabo@cisco.com> | 2017-05-04 11:12:35 +0200 |
commit | a101d966133a70b8a76526be25070436d14fcf9f (patch) | |
tree | 75e2dbf20de615e58252b780b2ba5baae8fdcf82 /vendor/github.com/bennyscetbun/jsongo/jsongo.go | |
parent | a968ead74525125dff9ae90b1c9a9102e4327900 (diff) |
initial commit
Signed-off-by: Rastislav Szabo <raszabo@cisco.com>
Diffstat (limited to 'vendor/github.com/bennyscetbun/jsongo/jsongo.go')
-rw-r--r-- | vendor/github.com/bennyscetbun/jsongo/jsongo.go | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/vendor/github.com/bennyscetbun/jsongo/jsongo.go b/vendor/github.com/bennyscetbun/jsongo/jsongo.go new file mode 100644 index 0000000..d661931 --- /dev/null +++ b/vendor/github.com/bennyscetbun/jsongo/jsongo.go @@ -0,0 +1,472 @@ +// Copyright 2014 Benny Scetbun. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +// Package Jsongo is a simple library to help you build Json without static struct +// +// Source code and project home: +// https://github.com/benny-deluxe/jsongo +// + +package jsongo + +import ( + "encoding/json" + "errors" + "reflect" + //"fmt" +) + +//ErrorKeyAlreadyExist error if a key already exist in current JSONNode +var ErrorKeyAlreadyExist = errors.New("jsongo key already exist") + +//ErrorMultipleType error if a JSONNode already got a different type of value +var ErrorMultipleType = errors.New("jsongo this node is already set to a different jsonNodeType") + +//ErrorArrayNegativeValue error if you ask for a negative index in an array +var ErrorArrayNegativeValue = errors.New("jsongo negative index for array") + +//ErrorArrayNegativeValue error if you ask for a negative index in an array +var ErrorAtUnsupportedType = errors.New("jsongo Unsupported Type as At argument") + +//ErrorRetrieveUserValue error if you ask the value of a node that is not a value node +var ErrorRetrieveUserValue = errors.New("jsongo Cannot retrieve node's value which is not of type value") + +//ErrorTypeUnmarshaling error if you try to unmarshal something in the wrong type +var ErrorTypeUnmarshaling = errors.New("jsongo Wrong type when Unmarshaling") + +//ErrorUnknowType error if you try to use an unknow JSONNodeType +var ErrorUnknowType = errors.New("jsongo Unknow JSONNodeType") + +//ErrorValNotPointer error if you try to use Val without a valid pointer +var ErrorValNotPointer = errors.New("jsongo: Val: arguments must be a pointer and not nil") + +//ErrorGetKeys error if you try to get the keys from a JSONNode that isnt a TypeMap or a TypeArray +var ErrorGetKeys = errors.New("jsongo: GetKeys: JSONNode is not a TypeMap or TypeArray") + +//ErrorDeleteKey error if you try to call DelKey on a JSONNode that isnt a TypeMap +var ErrorDeleteKey = errors.New("jsongo: DelKey: This JSONNode is not a TypeMap") + +//ErrorCopyType error if you try to call Copy on a JSONNode that isnt a TypeUndefined +var ErrorCopyType = errors.New("jsongo: Copy: This JSONNode is not a TypeUndefined") + +//JSONNode Datastructure to build and maintain Nodes +type JSONNode struct { + m map[string]*JSONNode + a []JSONNode + v interface{} + vChanged bool //True if we changed the type of the value + t JSONNodeType //Type of that JSONNode 0: Not defined, 1: map, 2: array, 3: value + dontExpand bool //dont expand while Unmarshal +} + +//JSONNodeType is used to set, check and get the inner type of a JSONNode +type JSONNodeType uint + +const ( + //TypeUndefined is set by default for empty JSONNode + TypeUndefined JSONNodeType = iota + //TypeMap is set when a JSONNode is a Map + TypeMap + //TypeArray is set when a JSONNode is an Array + TypeArray + //TypeValue is set when a JSONNode is a Value Node + TypeValue + //typeError help us detect errors + typeError +) + +//At helps you move through your node by building them on the fly +// +//val can be string or int only +// +//strings are keys for TypeMap +// +//ints are index in TypeArray (it will make array grow on the fly, so you should start to populate with the biggest index first)* +func (that *JSONNode) At(val ...interface{}) *JSONNode { + if len(val) == 0 { + return that + } + switch vv := val[0].(type) { + case string: + return that.atMap(vv, val[1:]...) + case int: + return that.atArray(vv, val[1:]...) + } + panic(ErrorAtUnsupportedType) +} + +//atMap return the JSONNode in current map +func (that *JSONNode) atMap(key string, val ...interface{}) *JSONNode { + if that.t != TypeUndefined && that.t != TypeMap { + panic(ErrorMultipleType) + } + if that.m == nil { + that.m = make(map[string]*JSONNode) + that.t = TypeMap + } + if next, ok := that.m[key]; ok { + return next.At(val...) + } + that.m[key] = new(JSONNode) + return that.m[key].At(val...) +} + +//atArray return the JSONNode in current TypeArray (and make it grow if necessary) +func (that *JSONNode) atArray(key int, val ...interface{}) *JSONNode { + if that.t == TypeUndefined { + that.t = TypeArray + } else if that.t != TypeArray { + panic(ErrorMultipleType) + } + if key < 0 { + panic(ErrorArrayNegativeValue) + } + if key >= len(that.a) { + newa := make([]JSONNode, key+1) + for i := 0; i < len(that.a); i++ { + newa[i] = that.a[i] + } + that.a = newa + } + return that.a[key].At(val...) +} + +//Map Turn this JSONNode to a TypeMap and/or Create a new element for key if necessary and return it +func (that *JSONNode) Map(key string) *JSONNode { + if that.t != TypeUndefined && that.t != TypeMap { + panic(ErrorMultipleType) + } + if that.m == nil { + that.m = make(map[string]*JSONNode) + that.t = TypeMap + } + if _, ok := that.m[key]; ok { + return that.m[key] + } + that.m[key] = &JSONNode{} + return that.m[key] +} + +//Array Turn this JSONNode to a TypeArray and/or set the array size (reducing size will make you loose data) +func (that *JSONNode) Array(size int) *[]JSONNode { + if that.t == TypeUndefined { + that.t = TypeArray + } else if that.t != TypeArray { + panic(ErrorMultipleType) + } + if size < 0 { + panic(ErrorArrayNegativeValue) + } + var min int + if size < len(that.a) { + min = size + } else { + min = len(that.a) + } + newa := make([]JSONNode, size) + for i := 0; i < min; i++ { + newa[i] = that.a[i] + } + that.a = newa + return &(that.a) +} + +//Val Turn this JSONNode to Value type and/or set that value to val +func (that *JSONNode) Val(val interface{}) { + if that.t == TypeUndefined { + that.t = TypeValue + } else if that.t != TypeValue { + panic(ErrorMultipleType) + } + rt := reflect.TypeOf(val) + var finalval interface{} + if val == nil { + finalval = &val + that.vChanged = true + } else if rt.Kind() != reflect.Ptr { + rv := reflect.ValueOf(val) + var tmp reflect.Value + if rv.CanAddr() { + tmp = rv.Addr() + } else { + tmp = reflect.New(rt) + tmp.Elem().Set(rv) + } + finalval = tmp.Interface() + that.vChanged = true + } else { + finalval = val + } + that.v = finalval +} + +//Get Return value of a TypeValue as interface{} +func (that *JSONNode) Get() interface{} { + if that.t != TypeValue { + panic(ErrorRetrieveUserValue) + } + if that.vChanged { + rv := reflect.ValueOf(that.v) + return rv.Elem().Interface() + } + return that.v +} + +//GetKeys Return a slice interface that represent the keys to use with the At fonction (Works only on TypeMap and TypeArray) +func (that *JSONNode) GetKeys() []interface{} { + var ret []interface{} + switch that.t { + case TypeMap: + nb := len(that.m) + ret = make([]interface{}, nb) + for key := range that.m { + nb-- + ret[nb] = key + } + case TypeArray: + nb := len(that.a) + ret = make([]interface{}, nb) + for nb > 0 { + nb-- + ret[nb] = nb + } + default: + panic(ErrorGetKeys) + } + return ret +} + +//Len Return the length of the current Node +// +// if TypeUndefined return 0 +// +// if TypeValue return 1 +// +// if TypeArray return the size of the array +// +// if TypeMap return the size of the map +func (that *JSONNode) Len() int { + var ret int + switch that.t { + case TypeMap: + ret = len(that.m) + case TypeArray: + ret = len(that.a) + case TypeValue: + ret = 1 + } + return ret +} + +//SetType Is use to set the Type of a node and return the current Node you are working on +func (that *JSONNode) SetType(t JSONNodeType) *JSONNode { + if that.t != TypeUndefined && that.t != t { + panic(ErrorMultipleType) + } + if t >= typeError { + panic(ErrorUnknowType) + } + that.t = t + switch t { + case TypeMap: + that.m = make(map[string]*JSONNode, 0) + case TypeArray: + that.a = make([]JSONNode, 0) + case TypeValue: + that.Val(nil) + } + return that +} + +//GetType Is use to Get the Type of a node +func (that *JSONNode) GetType() JSONNodeType { + return that.t +} + +//Copy Will set this node like the one in argument. this node must be of type TypeUndefined +// +//if deepCopy is true we will copy all the children recursively else we will share the children +// +//return the current JSONNode +func (that *JSONNode) Copy(other *JSONNode, deepCopy bool) *JSONNode { + if that.t != TypeUndefined { + panic(ErrorCopyType) + } + + if other.t == TypeValue { + *that = *other + } else if other.t == TypeArray { + if !deepCopy { + *that = *other + } else { + that.Array(len(other.a)) + for i := range other.a { + that.At(i).Copy(other.At(i), deepCopy) + } + } + } else if other.t == TypeMap { + that.SetType(other.t) + if !deepCopy { + for val := range other.m { + that.m[val] = other.m[val] + } + } else { + for val := range other.m { + that.Map(val).Copy(other.At(val), deepCopy) + } + } + } + return that +} + + +//Unset Will unset everything in the JSONnode. All the children data will be lost +func (that *JSONNode) Unset() { + *that = JSONNode{} +} + +//DelKey will remove a key in the map. +// +//return the current JSONNode. +func (that *JSONNode) DelKey(key string) *JSONNode { + if that.t != TypeMap { + panic(ErrorDeleteKey) + } + delete(that.m, key) + return that +} + +//UnmarshalDontExpand set or not if Unmarshall will generate anything in that JSONNode and its children +// +//val: will change the expanding rules for this node +// +//- The type wont be change for any type +// +//- Array wont grow +// +//- New keys wont be added to Map +// +//- Values set to nil "*.Val(nil)*" will be turn into the type decide by Json +// +//- It will respect any current mapping and will return errors if needed +// +//recurse: if true, it will set all the children of that JSONNode with val +func (that *JSONNode) UnmarshalDontExpand(val bool, recurse bool) *JSONNode { + that.dontExpand = val + if recurse { + switch that.t { + case TypeMap: + for k := range that.m { + that.m[k].UnmarshalDontExpand(val, recurse) + } + case TypeArray: + for k := range that.a { + that.a[k].UnmarshalDontExpand(val, recurse) + } + } + } + return that +} + +//MarshalJSON Make JSONNode a Marshaler Interface compatible +func (that *JSONNode) MarshalJSON() ([]byte, error) { + var ret []byte + var err error + switch that.t { + case TypeMap: + ret, err = json.Marshal(that.m) + case TypeArray: + ret, err = json.Marshal(that.a) + case TypeValue: + ret, err = json.Marshal(that.v) + default: + ret, err = json.Marshal(nil) + } + if err != nil { + return nil, err + } + return ret, err +} + +func (that *JSONNode) unmarshalMap(data []byte) error { + tmp := make(map[string]json.RawMessage) + err := json.Unmarshal(data, &tmp) + if err != nil { + return err + } + for k := range tmp { + if _, ok := that.m[k]; ok { + err := json.Unmarshal(tmp[k], that.m[k]) + if err != nil { + return err + } + } else if !that.dontExpand { + err := json.Unmarshal(tmp[k], that.Map(k)) + if err != nil { + return err + } + } + } + return nil +} + +func (that *JSONNode) unmarshalArray(data []byte) error { + var tmp []json.RawMessage + err := json.Unmarshal(data, &tmp) + if err != nil { + return err + } + for i := len(tmp) - 1; i >= 0; i-- { + if !that.dontExpand || i < len(that.a) { + err := json.Unmarshal(tmp[i], that.At(i)) + if err != nil { + return err + } + } + } + return nil +} + +func (that *JSONNode) unmarshalValue(data []byte) error { + if that.v != nil { + return json.Unmarshal(data, that.v) + } + var tmp interface{} + err := json.Unmarshal(data, &tmp) + if err != nil { + return err + } + that.Val(tmp) + return nil +} + +//UnmarshalJSON Make JSONNode a Unmarshaler Interface compatible +func (that *JSONNode) UnmarshalJSON(data []byte) error { + if len(data) == 0 { + return nil + } + if that.dontExpand && that.t == TypeUndefined { + return nil + } + if that.t == TypeValue { + return that.unmarshalValue(data) + } + if data[0] == '{' { + if that.t != TypeMap && that.t != TypeUndefined { + return ErrorTypeUnmarshaling + } + return that.unmarshalMap(data) + } + if data[0] == '[' { + if that.t != TypeArray && that.t != TypeUndefined { + return ErrorTypeUnmarshaling + } + return that.unmarshalArray(data) + + } + if that.t == TypeUndefined { + return that.unmarshalValue(data) + } + return ErrorTypeUnmarshaling +} |