jonson

package module
v0.0.0-...-d2f9c3c Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 30, 2018 License: Apache-2.0 Imports: 5 Imported by: 3

README

Jonson

Fast, lightweight, thread safe, dynamic type and schema-less golang utility and easy JSON handler

Table of Contents

  1. Quick start
  2. Getters
  3. Setters
  4. Constructors
  5. Types
  6. Mutators
  7. Converters
  8. Iterators
  9. Threads
  10. Dependencies
  11. License
  12. Contact

Install

go get github.com/KromDaniel/jonson

Quick start

import "github.com/KromDaniel/jonson"
Parsing and working with JSON
json, err := jonson.Parse([]byte(`{"foo": "bar", "arr": [1,2,"str", {"nestedFooA" : "nestedBar"}]}`))
if err != nil {
    // error handler
}
// Array mapper
json.At("arr").SliceMap(func(jsn *jonson.JSON, index int) *jonson.JSON {
    // JSON numbers are always float when parsed
    if jsn.IsFloat64() {
        return jonson.New(jsn.GetUnsafeFloat64() * float64(4))
    }
    if jsn.IsString() {
        return jonson.New("_" + jsn.GetUnsafeString())
    }

    if jsn.IsMap() {
        jsn.MapSet("me", []int{1, 2, 3})
    }
    return jsn
})
// {"arr":[4,8,"_str",{"me":[1,2,3],"nestedFooA":"nestedBar"}],"foo":"bar"}
fmt.Println(json.ToUnsafeJSONString())

Creating JSON from zero
json := jonson.NewEmptyJSONMap()
json.MapSet("arr", []interface{}{1, "str", []uint16{50,60,70}})
json.MapSet("numbers", []interface{}{})

for i:=0; i < 100; i++ {
    json.At("numbers").SliceAppend(i)
}

json.At("numbers").SliceFilter(func(jsn *jonson.JSON, index int) (shouldKeep bool) {
    return IsPrime(jsn.GetUnsafeInt())
})

// {"arr":[1,"str",[50,60,70]],"numbers":[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]}
fmt.Println(json.ToUnsafeJSONString())
Mutating
js := jonson.New([]interface{}{55.6, 70.8, 10.4, 1, "48", "-90"})

js.SliceMap(func(jsn *jonson.JSON, index int) *jonson.JSON {
    jsn.MutateToInt()
    return jsn
}).SliceMap(func(jsn *jonson.JSON, index int) *jonson.JSON {
    if jsn.GetUnsafeInt() > 50{
        jsn.MutateToString()
    }
    return jsn
})

fmt.Println(js.ToUnsafeJSONString()) // ["55","70",10,1,48,-90]
Deep Compare

left := New(5)
right := New("5")

EqualsDeep(left, right) // false

const exampleJSON = `
[
  {
    "key": 0.8215845637650305,
    "date": "2018-05-30T13:39:19.867Z"
  },
  {
    "key": 0.8773275487707828,
    "date": "2018-04-30T13:39:19.867Z"
  }
]`

left = ParseUnsafe([]byte(testJsonString))
right = ParseUnsafe([]byte(testJsonString))

EqualsDeep(left, right) // true

left.SliceAppend(56)

EqualsDeep(left, right) // false

Getters

Getters are the way to retreive the actual value of the JSON, Since jonson is thread safe, primitive value is cloned before returned

Is type check

Jonson supports most of the reflect types each Jonson object can be asked for IsType(t reflect.Kind) or directly e.g IsInt(), IsSlice.

A legal JSON value can be one of the following types:

  • string
  • number
  • object
  • array
  • boolean
  • null

Jonson supports the getters IsSlice IsMap and IsPrimitive for string, number, boolean and null.

Since there are many type of numbers, There's a getter for each type e.g IsUint8 IsFloat32 or IsNumber()

Note When parsing JSON string, the default value of a number is Float64

Example
json := jonson.New("hello")
json.IsString() // true
json.IsSlice() // false
json.IsInt() // false
json := jonson.New(67.98)
json.IsNumber() // true
Value type getters

Each of reflect.Kind type has a getter and unsafe getter, unsafe getter returns the zero value for that type if type is wrong

Example
json := jonson.New(96)
isInt, val := json.GetInt()
if isInt {
    // safe, it's int
}
json.GetUnsafeFloat64() //0 value
json.GetUnsafeSlice() // 0-length []
Methods
  • JSON.At(keys ...interface{}) returns a pointer to the current key, can be chained to null values
  • JSON.GetSlice() returns []*jonson.JSON
  • JSON.GetMap() returns map[string]*jonson.JSON
  • JSON.GetObjectKeys() returns []string if JSON is map else nil
  • JSON.GetSliceLen() returns int, the length of the slice if JSON is slice, else 0
Indexer (At method)

JSON.At method accepts int or string as argument, assuming string for map and int and slice, returns the zero value if wrong type

At Example
js := jonson.NewEmptyJSONMap()
js.At("KeyOfObjectWithArrayAsValue").At(12).At(54).At("key") // 12, 54 is index of slice, string is key of map
// Same as
js.At("KeyOfObjectWithArrayAsValue", 12, 54, "key")
// same as
js.At("KeyOfObjectWithArrayAsValue", 12).At(54, "key") 
// same as goes on...

Setters

Setters, just is it sounds, sets a value to current JSON

How set works

Since jonson is thread safe, it must be aware when trying to read or write a value, in order to gurantee that, value is deeply cloned, if value passed as pointer, the jonson will use the actual element it points to.

Note For better performance pass struct and map as pointer the deep clone will happen only once at the jonson cloner. Prefer using the jonson setters to avoid unnecessary operations

Methods
  • JSON.SetValue(v interface{}) sets the passed value to current JSON pointer, overrides the type and the existing value
  • JSON.MapSet(key string, v interface{}) sets value to current JSON as the current key (works only if current JSON is map type)
  • JSON.SliceAppend(v ...interface{}) append all given values to slice (works only if current JSON is slice type)
  • JSON.SliceAppendBegin(v ...interface{}) same as SliceAppend but at the start of the slice instead at the end
  • JSON.SliceSet(index int, v interface{}) overrides value at specific index on slice (works only if current JSON is slice type)
Example

Deep clone understanding:

json := jonson.NewEmptyJSON() // nil value
exampleMap := make(map[string]int)
exampleMap["1"] = 1
exampleMap["2"] = 2

json.Set(&exampleMap)
exampleMap["1"] = 4

// key 1 is different value, because setters do deep clone
fmt.Println(exampleMap) // map[1:4 2:2]
fmt.Println(json.ToUnsafeJSONString()) // {"1":1,"2":2}

Faster way to create map:

json := jonson.NewEmptyJSONMap()
json.MapSet("1", 1).MapSet("2" ,2)
Constructors

Constructors are the way to initialize a new JSON object

Methods
  • jonson.New(value interface{}) *JSON creates a new JSON containing the passed value
  • jonson.NewEmptyJSON() *JSON creates a new empty JSON with the value of nil
  • jonson.NewEmptyJSONMap() *JSON creates a new empty JSON with the value map[string]*JSON
  • jonson.NewEmptyJSONArray() *JSON creates a new empty JSON with the value 0 length slice
  • jonson.Parse([]byte) (error, *JSON) parses the byte (assumed to be UTF-8 JSON string)
  • jonson.ParseUnsafe([]byte) *JSON same as jonson.Parse but returns the jonson.NewEmptyJSON() if error

Types

Jonson supports all valid types for JSON, here's how it works:

Map

JSON Object (key, value) is valid only for strings key, it means that only map[string]interface{} will work, a map with none string keys, the key will be ignored

Example
keyMixedMap := make(map[interface{}]interface{})
keyMixedMap[1] = "key is integer"
keyMixedMap["key"] = "key is string"

fmt.Println(jonson.New(&keyMixedMap).ToUnsafeJSONString()) //{"key":"key is string"}
Struct

Struct behaves the same as with encoding/json

Only public fields are exported, the name of the field is the key on the struct, unless there's a field descriptors with json tag json:"customKey". Public key that tagged with json:"-" it is ignored.

Note When passing a struct to Jonson, it is immediately being "Jonsonized" means the keys are converted instantly

Example
type MyStruct struct {
    Public  string
    private string
    Custom  string `json:"customKey"`
    Ignored string `json:"-"`
}

structExample := jonson.New(&MyStruct{
    Public:  "public value",
    private: "private value",
    Custom:  "custom value",
    Ignored: "Ignored value",
})

fmt.Println(structExample.At("private").IsNil()) // true
fmt.Println(structExample.ToUnsafeJSONString())  // {"Public":"public value","customKey":"custom value"}
Slice

Slice is the array type of JSON, jonson supports all kind of slices, as long as each element is JSON legal

Mutators

Mutators is a group of methods that mutates the existing JSON to different type, all the methods return bool indicates if success.

JSON with type slice or map will automatically return false

Methods
  • JSON.MutateToInt() bool
  • JSON.MutateToFloat() bool
  • JSON.MutateToUnsignedInt() bool
  • JSON.MutateToString() bool

Note MutateToFloat() converts to type float64, MutateToInt(), MutateToUnsignedInt() and MutateToString() converts to int, uint and string

Converters

Converters is a group of methods that converts the JSON object without changing it

Methods
  • JSON.ToJSON() ([]byte, error) stringify the JSON to []byte
  • JSON.ToUnsafeJSON() []byte stringify the JSON, if error returns empty []byte
  • JSON.ToJSONString() (string, error)
  • JSON.ToUnsafeJSONString() string empty string if error
  • JSON.ToInterface() interface{} returns the entire JSON tree as interface
  • JSON.Clone() *JSON Deep clone the current JSON tree

Iterators

Iterators is a group of methods that allows iteration on slice or map, it accepts a function as argument for callback

The methods will do nothing is JSON is not slice or map (according the relevant method)

Note Using map or filter (Similar to other languages Array.map and Array.filter), It won't return a new copy of the slice or map, it will mutate the existing one

Methods
  • JSON.SliceForEach(cb func(jsn *JSON, index int)) Iterate on JSON slice
  • JSON.SliceMap(cb func(jsn *JSON, index int) *JSON) Iterate on JSON slice, replacing each element with returned JSON
  • JSON.SliceFilter(cb func(jsn *JSON, index int) bool) Iterate on JSON slice, removing element if cb returned false
  • JSON.ObjectForEach(cb func(jsn *JSON, key string)) Iterate on JSON map
  • JSON.ObjectMap(cb func(jsn *JSON, key string) *JSON) Iterate on JSON map, replacing each value with returned JSON
  • JSON.ObjectFilter(cb func(jsn *JSON, key string) bool) Iterate on JSON map, removing value if cb returned false
Example
jsn := jonson.NewEmptyJSONMap()

jsn.MapSet("keyA", []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
jsn.MapSet("KeyB", 1)
jsn.MapSet("KeyC", 2)
fmt.Println(jsn.ToUnsafeJSONString()) // {"KeyB":1,"KeyC":2,"keyA":[1,2,3,4,5,6,7,8,9,10]}

// Object Map, multiply integer values * 3
jsn.ObjectMap(func(jsn *jonson.JSON, key string) *jonson.JSON {
    if jsn.IsInt() {
        return jonson.New(jsn.GetUnsafeInt() * 3)
    }
    return jsn
    // iterate on the array, keep only evens
}).At("keyA").SliceFilter(func(jsn *jonson.JSON, index int) (shouldKeep bool) {
    shouldKeep = jsn.GetUnsafeInt()%2 == 0
    return
})
fmt.Println(jsn.ToUnsafeJSONString()) // {"KeyB":3,"KeyC":6,"keyA":[2,4,6,8,10]}

Threads

Jonson managed thread safety by it self, it using read-writer mutex sync.RWMutex allowing multple readers the same time

Example
func writer(jsn *jonson.JSON, wg *sync.WaitGroup) {
	for i :=0 ; i < 100000; i++ {
		jsn.SliceAppend(i)
	}
	wg.Done()
}

func reader(jsn *jonson.JSON, wg *sync.WaitGroup) {
	time.Sleep(time.Nanosecond * 1000)
	fmt.Println("Reader", jsn.GetSliceLen())
	wg.Done()
}

func main() {
	wg := sync.WaitGroup{}
	arr := jonson.NewEmptyJSONArray()
	wg.Add(5)
	go writer(arr, &wg)
	for i:=0; i < 4; i++{
		go reader(arr, &wg)
	}
	wg.Wait()
	fmt.Println("Final len", arr.GetSliceLen())
}

/* Output
Reader 5650
Reader 5651
Reader 5652
Reader 5651
Final len 100000
*/

Dependencies

Jonson is fully free from 3rd party dependencies, the unit tests are also free of any dependencies

License

Apache 2.0

Contact

For any question or contribution, feel free to contact me at kromdan@gmail.com

Documentation

Overview

Written by Daniel Krom 2018

Written by Daniel Krom 2018

// added ability to ignore lock

Written by Daniel Krom 2018

Written by Daniel Krom 2018

Written by Daniel Krom 2018

Written by Daniel Krom 2018

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EqualsDeep

func EqualsDeep(left *JSON, right *JSON) bool

Types

type JSON

type JSON struct {
	// contains filtered or unexported fields
}

func New

func New(value interface{}) *JSON

New Jonson Object with the value of the interface. the reference to interface is lost and it is deeply cloned

Possible types: Primitive Map[string]interface{} Slice struct

func NewEmptyJSON

func NewEmptyJSON() *JSON

NewEmptyJSON Creates a new empty Jonson object with null value

func NewEmptyJSONArray

func NewEmptyJSONArray() *JSON

NewEmptyJSONArray Creates a new empty Jonson object with empty array []

func NewEmptyJSONMap

func NewEmptyJSONMap() *JSON

NewEmptyJSONMap Creates a new empty Jonson object with empty map {}

func Parse

func Parse(data []byte) (jsn *JSON, err error)

Parse JSON returns err, nil if error

func ParseUnsafe

func ParseUnsafe(data []byte) (jsn *JSON)

ParseUnsafe JSON returns null json if error

func (*JSON) At

func (jsn *JSON) At(key interface{}, keys ...interface{}) *JSON

At returns the jonson value at some path can be chained or\and using multiple keys String key will assume the jonson is object int key will assume the jonson is slice if the path is wrong the empty json is returned

Jonson.ParseUnsafe([]byte("{\"foo\" : \"bar\"}")).At("keyThatDoesNotExists").At("subKey", 3, 6 ,90)

At("key","subKey",5, 7) equals to .At("key").At(5).At(7) equals to At("key",5).At(7)

func (*JSON) Clone

func (jsn *JSON) Clone() *JSON

Clone deep the jonson

func (*JSON) DeleteMapKey

func (jsn *JSON) DeleteMapKey(key string) *JSON

func (*JSON) GetBool

func (jsn *JSON) GetBool() (isBool bool, value bool)

func (*JSON) GetFloat32

func (jsn *JSON) GetFloat32() (isFloat32 bool, value float32)

func (*JSON) GetFloat64

func (jsn *JSON) GetFloat64() (isFloat64 bool, value float64)

func (*JSON) GetInt

func (jsn *JSON) GetInt() (isInt bool, value int)

func (*JSON) GetInt16

func (jsn *JSON) GetInt16() (isInt16 bool, value int16)

func (*JSON) GetInt32

func (jsn *JSON) GetInt32() (isInt32 bool, value int32)

func (*JSON) GetInt64

func (jsn *JSON) GetInt64() (isInt64 bool, value int64)

func (*JSON) GetInt8

func (jsn *JSON) GetInt8() (isInt8 bool, value int8)

func (*JSON) GetMap

func (jsn *JSON) GetMap() (isMap bool, value map[string]*JSON)

func (*JSON) GetObjectKeys

func (jsn *JSON) GetObjectKeys() []string

func (*JSON) GetSlice

func (jsn *JSON) GetSlice() (isSlice bool, value []*JSON)

func (*JSON) GetSliceLen

func (jsn *JSON) GetSliceLen() int

func (*JSON) GetString

func (jsn *JSON) GetString() (isString bool, value string)

func (*JSON) GetUint

func (jsn *JSON) GetUint() (isUint bool, value uint)

func (*JSON) GetUint16

func (jsn *JSON) GetUint16() (isUint16 bool, value uint16)

func (*JSON) GetUint32

func (jsn *JSON) GetUint32() (isUint32 bool, value uint32)

func (*JSON) GetUint64

func (jsn *JSON) GetUint64() (isUint64 bool, value uint64)

func (*JSON) GetUint8

func (jsn *JSON) GetUint8() (isUint8 bool, value uint8)

func (*JSON) GetUnsafeBool

func (jsn *JSON) GetUnsafeBool() (value bool)

func (*JSON) GetUnsafeFloat32

func (jsn *JSON) GetUnsafeFloat32() (value float32)

func (*JSON) GetUnsafeFloat64

func (jsn *JSON) GetUnsafeFloat64() (value float64)

func (*JSON) GetUnsafeInt

func (jsn *JSON) GetUnsafeInt() (value int)

func (*JSON) GetUnsafeInt16

func (jsn *JSON) GetUnsafeInt16() (value int16)

func (*JSON) GetUnsafeInt32

func (jsn *JSON) GetUnsafeInt32() (value int32)

func (*JSON) GetUnsafeInt64

func (jsn *JSON) GetUnsafeInt64() (value int64)

func (*JSON) GetUnsafeInt8

func (jsn *JSON) GetUnsafeInt8() (value int8)

func (*JSON) GetUnsafeMap

func (jsn *JSON) GetUnsafeMap() (value map[string]*JSON)

func (*JSON) GetUnsafeSlice

func (jsn *JSON) GetUnsafeSlice() (value []*JSON)

func (*JSON) GetUnsafeString

func (jsn *JSON) GetUnsafeString() (value string)

func (*JSON) GetUnsafeUint

func (jsn *JSON) GetUnsafeUint() (value uint)

func (*JSON) GetUnsafeUint16

func (jsn *JSON) GetUnsafeUint16() (value uint16)

func (*JSON) GetUnsafeUint32

func (jsn *JSON) GetUnsafeUint32() (value uint32)

func (*JSON) GetUnsafeUint64

func (jsn *JSON) GetUnsafeUint64() (value uint64)

func (*JSON) GetUnsafeUint8

func (jsn *JSON) GetUnsafeUint8() (value uint8)

func (*JSON) IsBool

func (jsn *JSON) IsBool() bool

func (*JSON) IsFloat32

func (jsn *JSON) IsFloat32() bool

func (*JSON) IsFloat64

func (jsn *JSON) IsFloat64() bool

func (*JSON) IsInt

func (jsn *JSON) IsInt() bool

IsInt -> is the value type is int (default int 64)

func (*JSON) IsInt16

func (jsn *JSON) IsInt16() bool

func (*JSON) IsInt32

func (jsn *JSON) IsInt32() bool

func (*JSON) IsInt64

func (jsn *JSON) IsInt64() bool

func (*JSON) IsInt8

func (jsn *JSON) IsInt8() bool

func (*JSON) IsMap

func (jsn *JSON) IsMap() bool

func (*JSON) IsNil

func (jsn *JSON) IsNil() bool

func (*JSON) IsNumber

func (jsn *JSON) IsNumber() bool

IsNumber returns a boolean indicates is the value is number, can be any valid number type

func (*JSON) IsPrimitive

func (jsn *JSON) IsPrimitive() bool

func (*JSON) IsSlice

func (jsn *JSON) IsSlice() bool

func (*JSON) IsString

func (jsn *JSON) IsString() bool

IsString -> is the value type is string

func (*JSON) IsType

func (jsn *JSON) IsType(p reflect.Kind) bool

IsType returns a boolean indicates if the Jonson value is that type

func (*JSON) IsUint

func (jsn *JSON) IsUint() bool

func (*JSON) IsUint16

func (jsn *JSON) IsUint16() bool

func (*JSON) IsUint32

func (jsn *JSON) IsUint32() bool

func (*JSON) IsUint64

func (jsn *JSON) IsUint64() bool

func (*JSON) IsUint8

func (jsn *JSON) IsUint8() bool

func (*JSON) MapSet

func (jsn *JSON) MapSet(key string, value interface{}) *JSON

Set a value to MapObject if key doesn't exists, it creates it

if current json is not map, it does nothing

func (*JSON) MutateToFloat

func (jsn *JSON) MutateToFloat() (success bool)

func (*JSON) MutateToInt

func (jsn *JSON) MutateToInt() (success bool)

func (*JSON) MutateToString

func (jsn *JSON) MutateToString() (success bool)

func (*JSON) MutateToUnsignedInt

func (jsn *JSON) MutateToUnsignedInt() (success bool)

func (*JSON) ObjectFilter

func (jsn *JSON) ObjectFilter(cb func(jsn *JSON, key string) (shouldKeep bool)) *JSON

iterates on object, removing each value that cb returns false

func (*JSON) ObjectForEach

func (jsn *JSON) ObjectForEach(cb func(jsn *JSON, key string)) *JSON

iterates on object

func (*JSON) ObjectKeyExists

func (jsn *JSON) ObjectKeyExists(key string) (exists bool)

func (*JSON) ObjectMap

func (jsn *JSON) ObjectMap(cb func(jsn *JSON, key string) *JSON) *JSON

iterates on object, replacing each value with new returned value

func (*JSON) Set

func (jsn *JSON) Set(v interface{}) *JSON

Sets a value to the current JSON, Makes a deep copy of the interface, removing the original reference

func (*JSON) SliceAppend

func (jsn *JSON) SliceAppend(value ...interface{}) *JSON

Append a value at the end of the slice

if current json slice, it does nothing

multiple values will append in the order of the values SliceAppend(1,2,3,4) -> [oldSlice..., 1,2,3,4]

func (*JSON) SliceAppendBegin

func (jsn *JSON) SliceAppendBegin(value ...interface{}) *JSON

Append a value at the start of the slice

if current json slice, it does nothing multiple values will append begin in the order of the values SliceAppend(1,2,3,4) -> [4,3,2,1, oldSlice...]

func (*JSON) SliceFilter

func (jsn *JSON) SliceFilter(cb func(jsn *JSON, index int) (shouldKeep bool)) *JSON

iterates on slice with filter callback, removes values that callback returned false

func (*JSON) SliceForEach

func (jsn *JSON) SliceForEach(cb func(jsn *JSON, index int)) *JSON

iterates on slice

func (*JSON) SliceMap

func (jsn *JSON) SliceMap(cb func(jsn *JSON, index int) *JSON) *JSON

iterates on slice with map callback, transforming the slice to new slice

func (*JSON) SliceSet

func (jsn *JSON) SliceSet(index int, value interface{}) *JSON

Sets a value at index to current slice

if value isn't slice, it does nothing User must make sure the length of the slice contains the index

func (*JSON) ToInterface

func (jsn *JSON) ToInterface() interface{}

ToInterface returns the entire jonson tree as interface of the value e.g [Jonson(5), Jonson("str"), Jonson(map[string]Jonson)] -> to [5, "str", map[string]interface{}]

func (*JSON) ToJSON

func (jsn *JSON) ToJSON() ([]byte, error)

ToJSON converts Jonson to byte array (serialize)

func (*JSON) ToJSONString

func (jsn *JSON) ToJSONString() (string, error)

ToJSONString converts Jonson to json string

func (*JSON) ToUnsafeJSON

func (jsn *JSON) ToUnsafeJSON() (data []byte)

ToUnsafeJSON converts Jonson to byte array (serialize) returns empty byte array if error

func (*JSON) ToUnsafeJSONString

func (jsn *JSON) ToUnsafeJSONString() string

ToUnsafeJSONString converts Jonson to json string returns empty string if error

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL