📜 ⬆️ ⬇️

Type casting on Go

I share a simple library that I constantly use. Go works well with JSON, but often there is a lack of a set of functions to cast an interface {} to some type. Even defining a canonical structure for marshaling JSON, over time you have to define an additional field, calling it Extra interface {}. That's about what we have in practice.

type Message struct { store bool Type string `json:"type"` Session string `json:"session,omitempty"` Data map[string]string `json:"data,omitempty"` Text string `json:"text,omitempty"` Name string `json:"name,omitempty"` Time int64 `json:"time,omitempty,string"` ServerId int64 `json:"serverId,omitempty,string"` Extra interface{} `json:"extra,omitempty"` } 


Why the canonical approach does not work well. The main source of JSON is JavaScript from a web browser. JavaScript represents all numbers as double. JavaScript may round int64, for example

  fmt.Println(time.Now().UnixNano()) 

will issue 1428308621182823638
 javascript:alert(1428308621182823638) 

and this is already

')
therefore, some numbers can be defined as strings using the `json:", string "` tag, but this will not work if the user does not put quotes around the number.

 package main import ( "encoding/json" "fmt" ) type X struct { Time int64 `json:"time,omitempty,string"` } func main() { var m1 map[string]interface{} e := json.Unmarshal([]byte(`{"x":1,"y":{}}`), &m1) fmt.Println(e, m1) var x X e = json.Unmarshal([]byte(`{"time":1}`), &x) fmt.Println(e, x) e = json.Unmarshal([]byte(`{"time":"1"}`), &x) fmt.Println(e, x) } 

Playground

{"Time": 1} - did not work

It’s hard to imagine how much effort the caliper of the web service spends, even when writing in bold italics in the documentation that the numbers need to be passed in quotes, people will still pass them on anyway.

The opposite situation is also possible, if you need numbers in JSON, then users can pass a number as a string, because (in PCP works) the <input /> tag returns the entered numbers as strings, which means strings can get into the JSON web service instead of numbers.

Often easier to write faster
  var m1 map[string]interface{} e := json.Unmarshal([]byte(`{"x":1,"y":{}}`), &m1) fmt.Println(e, m1) 


and there are not enough laconic functions to bring the interface {} to a string or [] interface {}, so I defined them in my pyraconv package.

My functions do not return nil. For example, pyraconv.ToStringArray () always returns [] string {} instead of nil, pyraconv.ToInt64 () - always returns one int64 parameter without an error, which means you can write int (pyraconv.ToInt64 (x))

I do not pretend that this way I should parse any JSON anytime, anywhere, but I find this code very useful.

List of features:

 func ToBool(i1 interface{}) bool 

ToBool converts interface {} to bool

 func ToInt64(i1 interface{}) int64 

ToInt64 converts interface {} to int64

 func ToInterfaceArray(i1 interface{}) []interface{} 

ToInterfaceArray converts interface {} to [] interface {} and does not return nil

 func ToInterfaceMap(i1 interface{}) map[string]interface{} 

ToInterfaceMap converts interface {} to map [string] interface {} and does not return nil

 func ToString(i1 interface{}) string 

ToString converts interface {} to string

 func ToStringArray(i1 interface{}) []string 

ToStringArray converts interface {} to [] string and does not return nil

 func ToStringMap(i1 interface{}) map[string]string 

ToStringMap converts interface {} to map [string] string and does not return nil

 func CloneObject(a, b interface{}) 

CloneObject creates a copy of the object using serialization from the gob package. It can be more efficient to transfer a copy of an object to a gorutin for parallel processing than to use a locking mechanism.

An example of a web service handler using pyraconv when processing JSON.

 func handle_ctrl_channel(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { b, e := ioutil.ReadAll(r.Body) if e != nil { fmt.Fprintf(w, `{ "error": true, "no":1 }`) return } var m1 map[string]interface{} e = json.Unmarshal(b, &m1) if e != nil { fmt.Fprintf(w, `{ "error": true, "no":2 }`) return } cmd := m1["cmd"] if cmd == "add" { id := pyraconv.ToString(m1["id"]) url := pyraconv.ToStringArray(m1["urls"]) service.UpdateStream(url, id) return } if cmd == "delete" { id := pyraconv.ToString(m1["id"]) service.DelStream(id) return } } } 


Fork pyraconv on health. Enjoy your meal.

 go get github.com/CossackPyra/pyraconv 

Source: https://habr.com/ru/post/255043/


All Articles