Приведение типов на Go
Я делюсь простой библиотекой, которую я постоянно использую. Go хорошо работает с JSON, но часто не хватает набора функций для приведения interface{} к какому-то типу. Даже определив канонично структуру для маршалинга JSON, со временем приходится определять дополнительное поле, назвав его Extra interface{}. Вот примерно, что мы имеем на практике. 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»` }
Почему каноничный подход плохо работает. Основной источник JSON это JavaScript из веб броузера. JavaScript все числа представляет как double. JavaScript может округлить int64, к примеру
fmt.Println (time.Now ().UnixNano ()) выдаст 1428308621182823638 javascript: alert (1428308621182823638) , а это ужепоэтому некоторые числа можно определять как строки, используя тег `json:», string»`, но это не будет работать, если пользователь не поставит вокруг числа кавычки.
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} — не сработало
Трудно представить сколько сил потратит суппорт веб сервиса, даже когда в документации написать жирным курсивом, что числа надо передавать в кавычках, то все равно люди будут их передавать и так и так.
Обратная ситуация тоже возможна, если вам нужны числа в JSON, то пользователи могут передать число как строку, ведь (в ПХП работает) тег возвращает введенные числа как строки, значит в JSON веб сервиса вместо чисел могут попасть строки.
Часто проще быстрей написать
var m1 map[string]interface{} e:= json.Unmarshal ([]byte (`{«x»:1, «y»:{}}`), &m1) fmt.Println (e, m1) и тут не хватает лаконичных функций для приведения interface{} к string или []interface{}, вот их я и определил в своем пакете pyraconv.
Мои функции не возвращают nil. К примеру, pyraconv.ToStringArray () всегда вернет []string{} вместо nil, pyraconv.ToInt64() — всегда вернет один параметр int64 без ошибки, а значит можно написать, int (pyraconv.ToInt64(x))
Я не претендую на то, что таким образом следует парсить любой JSON всегда и везде, но нахожу этот код весьма полезным.
Список функций:
func ToBool (i1 interface{}) bool ToBool конвертирует interface{} в bool func ToInt64(i1 interface{}) int64 ToInt64 конвертирует interface{} в int64 func ToInterfaceArray (i1 interface{}) []interface{} ToInterfaceArray конвертирует interface{} в []interface{} и не возвращает nil func ToInterfaceMap (i1 interface{}) map[string]interface{} ToInterfaceMap конвертирует interface{} в map[string]interface{} и не возвращает nil func ToString (i1 interface{}) string ToString конвертирует interface{} в string func ToStringArray (i1 interface{}) []string ToStringArray конвертирует interface{} в []string и не возвращает nil func ToStringMap (i1 interface{}) map[string]string ToStringMap конвертирует interface{} в map[string]string и не возвращает nil func CloneObject (a, b interface{}) CloneObject создает копию объекта используя сериализацию из пакета gob. Бывает эффективней передать копию объекта в гоурутину для параллельной обработки, чем использовать механизм блокировок.Пример хендлера веб сервиса с использованием pyraconv при обработке 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 } } } Форкайте pyraconv на здоровье. Приятного аппетита.
go get github.com/CossackPyra/pyraconv