github.com/TIBCOSoftware/flogo-lib@v0.5.9/core/mapper/exprmapper/json/gabs.go (about)

     1  /*
     2  Copyright (c) 2014 Ashley Jeffs
     3  
     4  Permission is hereby granted, free of charge, to any person obtaining a copy
     5  of this software and associated documentation files (the "Software"), to deal
     6  in the Software without restriction, including without limitation the rights
     7  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8  copies of the Software, and to permit persons to whom the Software is
     9  furnished to do so, subject to the following conditions:
    10  
    11  The above copyright notice and this permission notice shall be included in
    12  all copies or substantial portions of the Software.
    13  
    14  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    20  THE SOFTWARE.
    21  */
    22  
    23  // Package gabs implements a simplified wrapper around creating and parsing JSON.
    24  package json
    25  
    26  import (
    27  	"encoding/json"
    28  	"errors"
    29  	"fmt"
    30  	"io"
    31  	"io/ioutil"
    32  	"reflect"
    33  	"strings"
    34  )
    35  
    36  //--------------------------------------------------------------------------------------------------
    37  
    38  var (
    39  	// ErrOutOfBounds - Index out of bounds.
    40  	ErrOutOfBounds = errors.New("out of bounds")
    41  
    42  	// ErrNotObjOrArray - The target is not an object or array type.
    43  	ErrNotObjOrArray = errors.New("not an object or array")
    44  
    45  	// ErrNotObj - The target is not an object type.
    46  	ErrNotObj = errors.New("not an object")
    47  
    48  	// ErrNotArray - The target is not an array type.
    49  	ErrNotArray = errors.New("not an array")
    50  
    51  	// ErrPathCollision - Creating a path failed because an element collided with an existing value.
    52  	ErrPathCollision = errors.New("encountered value collision whilst building path")
    53  
    54  	// ErrInvalidInputObj - The input value was not a map[string]interface{}.
    55  	ErrInvalidInputObj = errors.New("invalid input object")
    56  
    57  	// ErrInvalidInputText - The input data could not be parsed.
    58  	ErrInvalidInputText = errors.New("input text could not be parsed")
    59  
    60  	// ErrInvalidPath - The filepath was not valid.
    61  	ErrInvalidPath = errors.New("invalid file path")
    62  
    63  	// ErrInvalidBuffer - The input buffer contained an invalid JSON string
    64  	ErrInvalidBuffer = errors.New("input buffer contained invalid JSON")
    65  )
    66  
    67  //--------------------------------------------------------------------------------------------------
    68  
    69  // Container - an internal structure that holds a reference to the core interface map of the parsed
    70  // json. Use this container to move context.
    71  type Container struct {
    72  	object interface{}
    73  }
    74  
    75  // Data - Return the contained data as an interface{}.
    76  func (g *Container) Data() interface{} {
    77  	return g.object
    78  }
    79  
    80  //--------------------------------------------------------------------------------------------------
    81  
    82  // Path - Search for a value using dot notation.
    83  func (g *Container) Path(path string) *Container {
    84  	return g.Search(strings.Split(path, ".")...)
    85  }
    86  
    87  // Search - Attempt to find and return an object within the JSON structure by specifying the
    88  // hierarchy of field names to locate the target. If the search encounters an array and has not
    89  // reached the end target then it will iterate each object of the array for the target and return
    90  // all of the results in a JSON array.
    91  func (g *Container) Search(hierarchy ...string) *Container {
    92  	var object interface{}
    93  
    94  	object = g.object
    95  	for target := 0; target < len(hierarchy); target++ {
    96  		if mmap, ok := object.(map[string]string); ok {
    97  			object = mmap[hierarchy[target]]
    98  		} else if mmap, ok := object.(map[string]interface{}); ok {
    99  			object = mmap[hierarchy[target]]
   100  		} else if marray, ok := object.([]interface{}); ok {
   101  			tmpArray := []interface{}{}
   102  			for _, val := range marray {
   103  				tmpGabs := &Container{val}
   104  				res := tmpGabs.Search(hierarchy[target:]...).Data()
   105  				if res != nil {
   106  					tmpArray = append(tmpArray, res)
   107  				}
   108  			}
   109  			if len(tmpArray) == 0 {
   110  				return &Container{nil}
   111  			}
   112  			return &Container{tmpArray}
   113  		} else {
   114  			if object != nil {
   115  				fmt.Println(reflect.TypeOf(object).Kind().String())
   116  				//if reflect.TypeOf(object).Kind() == reflect.Struct {
   117  				field, err := GetFieldByName(object, hierarchy[target])
   118  				if err != nil {
   119  					log.Errorf("no field name [%s] found in [%+v]", hierarchy[target], object)
   120  					return &Container{nil}
   121  				}
   122  				return &Container{field.Interface()}
   123  				//}
   124  			}
   125  			return &Container{nil}
   126  
   127  		}
   128  	}
   129  	return &Container{object}
   130  }
   131  
   132  // S - Shorthand method, does the same thing as Search.
   133  func (g *Container) S(hierarchy ...string) *Container {
   134  	return g.Search(hierarchy...)
   135  }
   136  
   137  // Exists - Checks whether a path exists.
   138  func (g *Container) Exists(hierarchy ...string) bool {
   139  	return g.Search(hierarchy...).Data() != nil
   140  }
   141  
   142  // ExistsP - Checks whether a dot notation path exists.
   143  func (g *Container) ExistsP(path string) bool {
   144  	return g.Exists(strings.Split(path, ".")...)
   145  }
   146  
   147  // Index - Attempt to find and return an object within a JSON array by index.
   148  func (g *Container) Index(index int) *Container {
   149  	if array, ok := g.Data().([]interface{}); ok {
   150  		if index >= len(array) {
   151  			return &Container{nil}
   152  		}
   153  		return &Container{array[index]}
   154  	}
   155  	return &Container{nil}
   156  }
   157  
   158  // Children - Return a slice of all the children of the array. This also works for objects, however,
   159  // the children returned for an object will NOT be in order and you lose the names of the returned
   160  // objects this way.
   161  func (g *Container) Children() ([]*Container, error) {
   162  	if array, ok := g.Data().([]interface{}); ok {
   163  		children := make([]*Container, len(array))
   164  		for i := 0; i < len(array); i++ {
   165  			children[i] = &Container{array[i]}
   166  		}
   167  		return children, nil
   168  	}
   169  	if mmap, ok := g.Data().(map[string]interface{}); ok {
   170  		children := []*Container{}
   171  		for _, obj := range mmap {
   172  			children = append(children, &Container{obj})
   173  		}
   174  		return children, nil
   175  	} else if mmap, ok := g.Data().(map[string]string); ok {
   176  		children := []*Container{}
   177  		for _, obj := range mmap {
   178  			children = append(children, &Container{obj})
   179  		}
   180  		return children, nil
   181  	}
   182  	return nil, ErrNotObjOrArray
   183  }
   184  
   185  // ChildrenMap - Return a map of all the children of an object.
   186  func (g *Container) ChildrenMap() (map[string]*Container, error) {
   187  	if mmap, ok := g.Data().(map[string]interface{}); ok {
   188  		children := map[string]*Container{}
   189  		for name, obj := range mmap {
   190  			children[name] = &Container{obj}
   191  		}
   192  		return children, nil
   193  	} else if mmap, ok := g.Data().(map[string]string); ok {
   194  		children := map[string]*Container{}
   195  		for name, obj := range mmap {
   196  			children[name] = &Container{obj}
   197  		}
   198  		return children, nil
   199  	}
   200  	return nil, ErrNotObj
   201  }
   202  
   203  //--------------------------------------------------------------------------------------------------
   204  
   205  // Set - Set the value of a field at a JSON path, any parts of the path that do not exist will be
   206  // constructed, and if a collision occurs with a non object type whilst iterating the path an error
   207  // is returned.
   208  func (g *Container) Set(value interface{}, path ...string) (*Container, error) {
   209  	if len(path) == 0 {
   210  		g.object = value
   211  		return g, nil
   212  	}
   213  	var object interface{}
   214  	if g.object == nil {
   215  		g.object = map[string]interface{}{}
   216  	}
   217  	object = g.object
   218  	for target := 0; target < len(path); target++ {
   219  		if mmap, ok := object.(map[string]interface{}); ok {
   220  			if target == len(path)-1 {
   221  				mmap[path[target]] = value
   222  			} else if mmap[path[target]] == nil {
   223  				mmap[path[target]] = map[string]interface{}{}
   224  			}
   225  			object = mmap[path[target]]
   226  		} else if mmap, ok := object.(map[string]string); ok {
   227  			if target == len(path)-1 {
   228  				v, ok := value.(string)
   229  				if !ok {
   230  					return &Container{nil}, fmt.Errorf("convert [%+v] to string failed", value)
   231  				}
   232  				mmap[path[target]] = v
   233  			} else if mmap[path[target]] == "" {
   234  				mmap[path[target]] = ""
   235  			}
   236  			object = mmap[path[target]]
   237  		} else {
   238  			field, err := GetFieldByName(object, path[target])
   239  			if target == len(path)-1 {
   240  				if err != nil {
   241  					return &Container{nil}, fmt.Errorf("not found name [%s] in struct [%+v]", path[target], field)
   242  				}
   243  				field.Set(reflect.ValueOf(value))
   244  			} else {
   245  				if err != nil {
   246  					return &Container{nil}, fmt.Errorf("not found name [%s] in struct [%+v]", path[target], field)
   247  				}
   248  
   249  				if field.Interface() == nil {
   250  					field.Set(reflect.New(field.Type()))
   251  				}
   252  			}
   253  			object = field
   254  
   255  		}
   256  	}
   257  	return &Container{object}, nil
   258  }
   259  
   260  // SetP - Does the same as Set, but using a dot notation JSON path.
   261  func (g *Container) SetP(value interface{}, path string) (*Container, error) {
   262  	return g.Set(value, strings.Split(path, ".")...)
   263  }
   264  
   265  // SetIndex - Set a value of an array element based on the index.
   266  func (g *Container) SetIndex(value interface{}, index int) (*Container, error) {
   267  	if array, ok := g.Data().([]interface{}); ok {
   268  		if index >= len(array) {
   269  			return &Container{nil}, ErrOutOfBounds
   270  		}
   271  		array[index] = value
   272  		return &Container{array[index]}, nil
   273  	}
   274  	return &Container{nil}, ErrNotArray
   275  }
   276  
   277  // Object - Create a new JSON object at a path. Returns an error if the path contains a collision
   278  // with a non object type.
   279  func (g *Container) Object(path ...string) (*Container, error) {
   280  	return g.Set(map[string]interface{}{}, path...)
   281  }
   282  
   283  // ObjectP - Does the same as Object, but using a dot notation JSON path.
   284  func (g *Container) ObjectP(path string) (*Container, error) {
   285  	return g.Object(strings.Split(path, ".")...)
   286  }
   287  
   288  // ObjectI - Create a new JSON object at an array index. Returns an error if the object is not an
   289  // array or the index is out of bounds.
   290  func (g *Container) ObjectI(index int) (*Container, error) {
   291  	return g.SetIndex(map[string]interface{}{}, index)
   292  }
   293  
   294  // Array - Create a new JSON array at a path. Returns an error if the path contains a collision with
   295  // a non object type.
   296  func (g *Container) Array(path ...string) (*Container, error) {
   297  	return g.Set([]interface{}{}, path...)
   298  }
   299  
   300  // ArrayP - Does the same as Array, but using a dot notation JSON path.
   301  func (g *Container) ArrayP(path string) (*Container, error) {
   302  	return g.Array(strings.Split(path, ".")...)
   303  }
   304  
   305  // ArrayI - Create a new JSON array at an array index. Returns an error if the object is not an
   306  // array or the index is out of bounds.
   307  func (g *Container) ArrayI(index int) (*Container, error) {
   308  	return g.SetIndex([]interface{}{}, index)
   309  }
   310  
   311  // ArrayOfSize - Create a new JSON array of a particular size at a path. Returns an error if the
   312  // path contains a collision with a non object type.
   313  func (g *Container) ArrayOfSize(size int, path ...string) (*Container, error) {
   314  	a := make([]interface{}, size)
   315  	return g.Set(a, path...)
   316  }
   317  
   318  // ArrayOfSizeP - Does the same as ArrayOfSize, but using a dot notation JSON path.
   319  func (g *Container) ArrayOfSizeP(size int, path string) (*Container, error) {
   320  	return g.ArrayOfSize(size, strings.Split(path, ".")...)
   321  }
   322  
   323  // ArrayOfSizeI - Create a new JSON array of a particular size at an array index. Returns an error
   324  // if the object is not an array or the index is out of bounds.
   325  func (g *Container) ArrayOfSizeI(size, index int) (*Container, error) {
   326  	a := make([]interface{}, size)
   327  	return g.SetIndex(a, index)
   328  }
   329  
   330  // Delete - Delete an element at a JSON path, an error is returned if the element does not exist.
   331  func (g *Container) Delete(path ...string) error {
   332  	var object interface{}
   333  
   334  	if g.object == nil {
   335  		return ErrNotObj
   336  	}
   337  	object = g.object
   338  	for target := 0; target < len(path); target++ {
   339  		if mmap, ok := object.(map[string]interface{}); ok {
   340  			if target == len(path)-1 {
   341  				if _, ok := mmap[path[target]]; ok {
   342  					delete(mmap, path[target])
   343  				} else {
   344  					return ErrNotObj
   345  				}
   346  			}
   347  			object = mmap[path[target]]
   348  		} else if mmap, ok := object.(map[string]string); ok {
   349  			if target == len(path)-1 {
   350  				if _, ok := mmap[path[target]]; ok {
   351  					delete(mmap, path[target])
   352  				} else {
   353  					return ErrNotObj
   354  				}
   355  			}
   356  			object = mmap[path[target]]
   357  		} else {
   358  			return ErrNotObj
   359  		}
   360  	}
   361  	return nil
   362  }
   363  
   364  // DeleteP - Does the same as Delete, but using a dot notation JSON path.
   365  func (g *Container) DeleteP(path string) error {
   366  	return g.Delete(strings.Split(path, ".")...)
   367  }
   368  
   369  //--------------------------------------------------------------------------------------------------
   370  
   371  /*
   372  Array modification/search - Keeping these options simple right now, no need for anything more
   373  complicated since you can just cast to []interface{}, modify and then reassign with Set.
   374  */
   375  
   376  // ArrayAppend - Append a value onto a JSON array.
   377  func (g *Container) ArrayAppend(value interface{}, path ...string) error {
   378  	array, ok := g.Search(path...).Data().([]interface{})
   379  	if !ok {
   380  		return ErrNotArray
   381  	}
   382  	array = append(array, value)
   383  	_, err := g.Set(array, path...)
   384  	return err
   385  }
   386  
   387  // ArrayAppendP - Append a value onto a JSON array using a dot notation JSON path.
   388  func (g *Container) ArrayAppendP(value interface{}, path string) error {
   389  	return g.ArrayAppend(value, strings.Split(path, ".")...)
   390  }
   391  
   392  // ArrayRemove - Remove an element from a JSON array.
   393  func (g *Container) ArrayRemove(index int, path ...string) error {
   394  	if index < 0 {
   395  		return ErrOutOfBounds
   396  	}
   397  	array, ok := g.Search(path...).Data().([]interface{})
   398  	if !ok {
   399  		return ErrNotArray
   400  	}
   401  	if index < len(array) {
   402  		array = append(array[:index], array[index+1:]...)
   403  	} else {
   404  		return ErrOutOfBounds
   405  	}
   406  	_, err := g.Set(array, path...)
   407  	return err
   408  }
   409  
   410  // ArrayRemoveP - Remove an element from a JSON array using a dot notation JSON path.
   411  func (g *Container) ArrayRemoveP(index int, path string) error {
   412  	return g.ArrayRemove(index, strings.Split(path, ".")...)
   413  }
   414  
   415  // ArrayElement - Access an element from a JSON array.
   416  func (g *Container) ArrayElement(index int, path ...string) (*Container, error) {
   417  	if index < 0 {
   418  		return &Container{nil}, ErrOutOfBounds
   419  	}
   420  	array, ok := g.Search(path...).Data().([]interface{})
   421  	if !ok {
   422  		//Convert to array
   423  		var err error
   424  		array, err = ToArray(g.Search(path...).Data())
   425  		if err != nil {
   426  			return &Container{nil}, ErrNotArray
   427  		}
   428  	}
   429  	if index < len(array) {
   430  		return &Container{array[index]}, nil
   431  	}
   432  	return &Container{nil}, ErrOutOfBounds
   433  }
   434  
   435  // ArrayElementP - Access an element from a JSON array using a dot notation JSON path.
   436  func (g *Container) ArrayElementP(index int, path string) (*Container, error) {
   437  	return g.ArrayElement(index, strings.Split(path, ".")...)
   438  }
   439  
   440  // ArrayCount - Count the number of elements in a JSON array.
   441  func (g *Container) ArrayCount(path ...string) (int, error) {
   442  	if array, ok := g.Search(path...).Data().([]interface{}); ok {
   443  		return len(array), nil
   444  	}
   445  	return 0, ErrNotArray
   446  }
   447  
   448  // ArrayCountP - Count the number of elements in a JSON array using a dot notation JSON path.
   449  func (g *Container) ArrayCountP(path string) (int, error) {
   450  	return g.ArrayCount(strings.Split(path, ".")...)
   451  }
   452  
   453  //--------------------------------------------------------------------------------------------------
   454  
   455  // Bytes - Converts the contained object back to a JSON []byte blob.
   456  func (g *Container) Bytes() []byte {
   457  	if g.object != nil {
   458  		if bytes, err := json.Marshal(g.object); err == nil {
   459  			return bytes
   460  		}
   461  	}
   462  	return []byte("{}")
   463  }
   464  
   465  // BytesIndent - Converts the contained object to a JSON []byte blob formatted with prefix, indent.
   466  func (g *Container) BytesIndent(prefix string, indent string) []byte {
   467  	if g.object != nil {
   468  		if bytes, err := json.MarshalIndent(g.object, prefix, indent); err == nil {
   469  			return bytes
   470  		}
   471  	}
   472  	return []byte("{}")
   473  }
   474  
   475  // String - Converts the contained object to a JSON formatted string.
   476  func (g *Container) String() string {
   477  	return string(g.Bytes())
   478  }
   479  
   480  // StringIndent - Converts the contained object back to a JSON formatted string with prefix, indent.
   481  func (g *Container) StringIndent(prefix string, indent string) string {
   482  	return string(g.BytesIndent(prefix, indent))
   483  }
   484  
   485  // New - Create a new gabs JSON object.
   486  func New() *Container {
   487  	return &Container{map[string]interface{}{}}
   488  }
   489  
   490  // Consume - Gobble up an already converted JSON object, or a fresh map[string]interface{} object.
   491  func Consume(root interface{}) (*Container, error) {
   492  	return &Container{root}, nil
   493  }
   494  
   495  // ParseJSON - Convert a string into a representation of the parsed JSON.
   496  func ParseJSON(sample []byte) (*Container, error) {
   497  	var gabs Container
   498  
   499  	if err := json.Unmarshal(sample, &gabs.object); err != nil {
   500  		return nil, err
   501  	}
   502  
   503  	return &gabs, nil
   504  }
   505  
   506  // ParseJSONDecoder - Convert a json.Decoder into a representation of the parsed JSON.
   507  func ParseJSONDecoder(decoder *json.Decoder) (*Container, error) {
   508  	var gabs Container
   509  
   510  	if err := decoder.Decode(&gabs.object); err != nil {
   511  		return nil, err
   512  	}
   513  
   514  	return &gabs, nil
   515  }
   516  
   517  // ParseJSONFile - Read a file and convert into a representation of the parsed JSON.
   518  func ParseJSONFile(path string) (*Container, error) {
   519  	if len(path) > 0 {
   520  		cBytes, err := ioutil.ReadFile(path)
   521  		if err != nil {
   522  			return nil, err
   523  		}
   524  
   525  		container, err := ParseJSON(cBytes)
   526  		if err != nil {
   527  			return nil, err
   528  		}
   529  
   530  		return container, nil
   531  	}
   532  	return nil, ErrInvalidPath
   533  }
   534  
   535  // ParseJSONBuffer - Read the contents of a buffer into a representation of the parsed JSON.
   536  func ParseJSONBuffer(buffer io.Reader) (*Container, error) {
   537  	var gabs Container
   538  	jsonDecoder := json.NewDecoder(buffer)
   539  	if err := jsonDecoder.Decode(&gabs.object); err != nil {
   540  		return nil, err
   541  	}
   542  
   543  	return &gabs, nil
   544  }
   545  
   546  //--------------------------------------------------------------------------------------------------