I have a json response from a api as map[message:Login Success. userid:1]
Server:
c.JSON(200, gin.H{"message": "Login Success.", "userid": 1})
Client:
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
msg, ok := result["message"].(string)
if !ok {
msg = "Something went wrong."
}
userID, ok := result["userid"].(int)
if !ok {
userID = 0
}
But userID, ok := result["userid"].(int) always fails. I've even tried using:
switch v := x.(type) {
case nil:
fmt.Println("x is nil")
case int:
fmt.Println("x is", v)
case bool, string:
fmt.Println("x is bool or string")
default:
fmt.Println("type unknown")
}
And it just gave me unknown. Why is the integer not being taken as integer?
It looks like its treating the value as float64.
Here you can find the explanation why you are getting float64 and not int:
Check doc for Decode
See the documentation for Unmarshal for details about the conversion of JSON into a Go value.
And there see
To unmarshal JSON into an interface value, Unmarshal stores one of these in the interface value:
float64, for JSON numbers
Related
How do I iterate over the map of interfaces below to get the values of the interface returned in the map?
I have read through the extensive list of questions regarding iteration in Go but they didn't help me.
// https://api.kraken.com/0/public/AssetPairs
pairsResult, err := api.Query("AssetPairs", map[string]string{
})
if err != nil {
log.Fatal(err)
}
ks := reflect.ValueOf(pairsResult).MapKeys()
fmt.Printf(" %+v ", pairsResult) // result A below
fmt.Printf(" %+v ", ks) // result B below
// the value here is the value of MapKeys, which is the key
for kix, key := range ks {
fmt.Printf(" %+v %+v \n ", kix, key) // result C below
}
Result A
map[AAVEETH:map[aclass_base:currency aclass_quote:currency altname:AAVEETH base:AAVE fee_volume_currency:ZUSD fees:[[0 0.26] [50000 0.24] [100000 0.22] [250000 0.2] [500000 0.18]...
Result B
[KEEPXBT LINKUSD LINKXBT NANOEUR ...]
Result C
0 KEEPXBT 1 LINKUSD 2 LINKXBT 3 NANOEUR 4 USDTAUD ...
This is the source of the API wrapper function that is being called above
// Query sends a query to Kraken api for given method and parameters
func (api *KrakenAPI) Query(method string, data map[string]string) (interface{}, error) {
values := url.Values{}
for key, value := range data {
values.Set(key, value)
}
// Check if method is public or private
if isStringInSlice(method, publicMethods) {
return api.queryPublic(method, values, nil)
} else if isStringInSlice(method, privateMethods) {
return api.queryPrivate(method, values, nil)
}
return nil, fmt.Errorf("Method '%s' is not valid", method)
}
This happens when I try to iterate over the value:
This happens when I try to iterate over the initial result:
Assuming you're using this and from looking a bit into its source it seems to me that the concrete type of the result will be map[string]interface{}, if that is the case then you can do this.
res, err := api.Query("AssetPairs", map[string]string{})
if err != nil {
log.Fatal(err)
}
pairs, ok := res.(map[string]interface{})
if !ok {
log.Fatal("unsupported type")
}
for k, v := range pairs {
fmt.Printf("key=%s value=%+v\n ", k, v)
}
As the previous response mentions, we see that the interface returned becomes a map[string]interface{}, the following code would do the trick to retrieve the types:
for _, v := range d.(map[string]interface{}) {
switch v.(type) {
case map[string]interface{}:
fmt.Println("Its another map of string interface")
case interface{}:
fmt.Println("its an interface")
case string:
fmt.Println("its a string")
case []string:
fmt.Println("its a string array")
case float32:
fmt.Println("its a float32")
case float64:
fmt.Println("its a float64")
default:
fmt.Printf("Different thing, %T\n", v)
}
}
Code here:
https://play.golang.org/p/81LLYSvJVf8
However, I would recommend use explicit type, that would make your life much easier:
// Generated by https://quicktype.io
type KrakenTypes struct {
Error []interface{} `json:"error"`
Result map[string]Result `json:"result"`
}
type Result struct {
Altname string `json:"altname"`
Wsname *string `json:"wsname,omitempty"`
AclassBase Aclass `json:"aclass_base"`
Base string `json:"base"`
AclassQuote Aclass `json:"aclass_quote"`
Quote FeeVolumeCurrency `json:"quote"`
Lot Lot `json:"lot"`
PairDecimals int64 `json:"pair_decimals"`
LotDecimals int64 `json:"lot_decimals"`
LotMultiplier int64 `json:"lot_multiplier"`
LeverageBuy []int64 `json:"leverage_buy"`
LeverageSell []int64 `json:"leverage_sell"`
Fees [][]float64 `json:"fees"`
FeesMaker [][]float64 `json:"fees_maker"`
FeeVolumeCurrency FeeVolumeCurrency `json:"fee_volume_currency"`
MarginCall int64 `json:"margin_call"`
MarginStop int64 `json:"margin_stop"`
Ordermin *string `json:"ordermin,omitempty"`
}
Here we can use json decoding once the response was read, so the iterations to find out the types of each level could be avoided and we can just access to each member directly.
Complete code here: https://play.golang.org/p/v3tlroyx1mW
I the following issue: i receive information in yaml form, it can come in 2 configurations-
asingle character:
Character:
Name: Stackoverflow
Type: Awesome
Or in a slice of multiple characters with an optional field of slice of friends:
Character:
- Name: Stackoverflow
Type: Awesome
Friends: [Ben, John]
- Name: Facebook
Type: Ok
Because I don't know which configuration will arrive I tried to unmarshal the yaml to interface{} and then cast it to one of the structs but it didn't worked out:
type CharacterConfig struct{
Name string `yaml:"Name"`
Type string `yaml:"Type"`
Friends []string `yaml:"Friends,omitempty"`
var d interface{}
err := yaml.Unmarshal([]byte(yamlData), &d)
if err != nil {
panic(err)
}
res, ok := d.(CharacterConfig)
if ok {
fmt.Println("CharacterConfig are ok")
}
res, ok := d.([]CharacterConfig)
if ok {
fmt.Println("[]CharacterConfig are ok")
}
But I don't receive any of the prints... when I debug is I can see the unmarshal into interface worked, but non of the castings.
I know I can just unmarshal straight into the structs, but I don't understand wht what i did didn't work.
Unmarshaling into interface{} will not magically guess you want the result in your custom CharacterConfig struct. Unmarshaling YAML into interface{} will use map[interface{}]interface{} to represent objects, and []interface{} to represent lists / arrays, recursively.
So the type assertions above yield ok = false because the values stored in them are maps or slices of interfaces{}. You may type assert those types, and the assertion will succeed, but you won't be any closer in getting those values as your structs.
Also note that your input YAML needs another wrapper layer:
type Character struct {
CC CharacterConfig `yaml:"Character"`
}
type CharacterList struct {
CCs []CharacterConfig `yaml:"Character"`
}
So an easy way would be to first try to unmarshal into a value of type Character, and if that succeeds, you're done. If not, try to unmarshal again, this time into CharacterList.
Here's an example code that does that:
func unmarshal(data []byte) {
var c Character
err := yaml.Unmarshal(data, &c)
if err == nil {
fmt.Printf("It's Character: %+v\n", c)
return
}
var cl CharacterList
err = yaml.Unmarshal(data, &cl)
if err != nil {
panic(err)
}
fmt.Printf("It's CharacterList: %+v\n", cl)
}
Testing it like this:
func main() {
unmarshal([]byte(src))
unmarshal([]byte(src2))
}
const src = `Character:
Name: Stackoverflow
Type: Awesome`
const src2 = `Character:
- Name: Stackoverflow
Type: Awesome
Friends: [Ben, John]
- Name: Facebook
Type: Ok`
Output will be (try it on the Go Playground):
It's Character: {CC:{Name:Stackoverflow Type:Awesome Friends:[]}}
It's CharacterList: {CCs:[{Name:Stackoverflow Type:Awesome Friends:[Ben John]} {Name:Facebook Type:Ok Friends:[]}]}
Similar to what you did but without interface:
type CharacterConfig struct{
Name string `yaml:"Name"`
Type string `yaml:"Type"`
Friends []string `yaml:"Friends,omitempty"`
}
var a CharacterConfig
var b []CharacterConfig
err := yaml.Unmarshal([]byte(yamlData), &a)
if err == nil {
// a is valid format
}
err = yaml.Unmarshal([]byte(yamlData), &b)
if err == nil {
// b is valid format
}
I am trying to create a CRUD and validating my request body using a Go library called [validator][1]
Validation Code sample:
func (v *Validation) Validate(i interface{}) ValidationErrors {
errs := v.validate.Struct(i).(validator.ValidationErrors) // panic originates here
if len(errs) == 0 {
return nil
}
var returnErrs []ValidationError
for _, err := range errs {
// cast the FieldError into our ValidationError and append to the slice
ve := ValidationError{err.(validator.FieldError)}
returnErrs = append(returnErrs, ve)
}
return returnErrs
}
The above validator works for invalid request body such as invalid ID.
But for a valid body, it initiates a panic
Stack Trace:
products-api 2020/07/12 15:15:11 http: panic serving 127.0.0.1:33288: interface conversion: error is nil, not validator.ValidationErrors
goroutine 21 [running]:
net/http.(*conn).serve.func1(0xc0003b6140)
/usr/local/go/src/net/http/server.go:1772 +0x139
panic(0x93d6c0, 0xc0003845d0)
/usr/local/go/src/runtime/panic.go:973 +0x3e3
github.com/AymanArif/golang-microservices/data.(*Validation).Validate(0xc0005a8108, 0x8f3480, 0xc0005aa2c0, 0x0, 0x0, 0x203000)
/home/ayman/Desktop/golang-microservices/data/validation.go:70 +0x211
interface conversion: interface conversion: error is nil, not validator.ValidationErrors
Create REST Logic:
func (p *Products) Create(rw http.ResponseWriter, r *http.Request) {
prod := r.Context().Value(KeyProduct{}).(data.Product) // Panic originate here. Check below for struct definiton
p.l.Printf("[DEBUG] Inserting product: %#v\n", prod)
data.AddProduct(prod)
}
// data.Product
type Product struct {
ID int `json:"id"` // Unique identifier for the product
Name string `json:"name" validate:"required"`
Description string `json:"description"`
SKU string `json:"sku" validate:"sku"`
}
How can I do error handling for correct requests?
[1]: https://github.com/go-playground/validator
The error you got is eloquent:
interface conversion: interface {} is *data.Product, not data.Product
The line r.Context().Value(KeyProduct{}) returns an interface type interface{}, which, the error tells you, is holding a value of concrete type *data.Product (pointer to data.Product)
Instead, you are attempting to convert it to a data.Product, without checking whether the conversion is valid.
Replace the line with:
prod := r.Context().Value(KeyProduct{}).(*data.Product)
You might want to read the Go Tour about type assertions.
After your update, the error you have now is still the same kind of issue:
interface conversion: error is nil, not validator.ValidationErrors
With the expression err.(validator.FieldError) you are attempting to convert err to a validator.FieldError when err is in fact nil.
The previous check len(errs) == 0 is only verifying that errs length is not zero, but the slice might be of non-zero length and contain nil values.
You can instead test the type assertion:
if fe, ok := err.(validator.FieldError); ok {
ve := ValidationError{fe}
returnErrs = append(returnErrs, ve)
}
If you re write the Validate function to something like this it should work. Type assertion needs to be checked for errs & err
func (v *Validation) Validate(i interface{}) ValidationErrors {
var returnErrs []ValidationError
if errs, ok := v.validate.Struct(i).(validator.ValidationErrors); ok {
if errs != nil {
for _, err := range errs {
if fe, ok := err.(validator.FieldError); ok {
ve := ValidationError{fe}
returnErrs = append(returnErrs, ve)
}
}
}
}
return returnErrs
}
I've got the following method:
func ValidateParam(conf map[string]interface{}, paramName string, out interface{}) error {
param, ok := conf[paramName]
if !ok {
return errors.New("some error")
}
// ...
}
I would like to be able to call it like so:
myVar := "some text"
err := ValidateParam(conf, "my_var_param", &myVar)
myOtherVar := &MyStruct{}
err := ValidateParam(conf, "my_struct_param", myOtherVar)
The idea is:
Get the param using the conf map
Check that this param could be converted into the same type as out
Hydrate out using the param
=> It is kind of the same process as for json.Unmarshal(data, &myVar) or when doing a query with mgo query.Collection("col").One(&myVar)
I can't find how to achieve this, any help would be more than welcome.
Cheers
One option is to use the reflect package:
The basic idea is to create reflect.Values for input and output, check if input is assignable to output and then assign.
func ValidateParam(conf map[string]interface{}, paramName string, out interface{}) error {
param, ok := conf[paramName]
if !ok {
return errors.New("some error")
}
// Output is pointer to value.
vo := reflect.ValueOf(out)
if vo.Kind() != reflect.Ptr {
return errors.New("out must be poitner")
}
vo = vo.Elem() // deref ptr
// Can input be assigned to output?
vi := reflect.ValueOf(param)
if !vi.Type().AssignableTo(vo.Type()) {
return fmt.Errorf("param %s of type %v is not assignable to %v", paramName, vi.Type(), vo.Type())
}
vo.Set(vi)
return nil
}
playground example
This is my struct, when I get a socket message I readJson and the structs gets filled with the data and all is fine. It goes through some functions, but once it goes through the Send function it serializes it in a weird way that eventually I get back a bunch of numbers and when I convert it to string, data is missing.
type Reply struct {
Topic string `redis:"topic" json:"topic"`
Ref string `redis:"ref" json:"ref"`
Payload struct {
Status string `redis:"status" json:"status"`
Response map[string]interface{} `redis:"response" json:"response"`
} `json:"payload"`
}
I just want to broadcast messages in this format.
This is where I get the modified and problematic data
func (rr *redisReceiver) run() error {
l := log.WithField("channel", Channel)
conn := rr.pool.Get()
defer conn.Close()
psc := redis.PubSubConn{Conn: conn}
psc.Subscribe(Channel)
go rr.connHandler()
for {
switch v := psc.Receive().(type) {
case redis.Message:
rr.broadcast(v.Data)
case redis.Subscription:
l.WithFields(logrus.Fields{
"kind": v.Kind,
"count": v.Count,
}).Println("Redis Subscription Received")
log.Println("Redis Subscription Received")
case error:
return errors.New("Error while subscribed to Redis channel")
default:
l.WithField("v", v).Info("Unknown Redis receive during subscription")
log.Println("Unknown Redis receive during subscription")
}
}
}
Does Redigo not support that type of data structure?
This is the format I get and the format I'm supposed to get.
//Get
"{{spr_reply sketchpad map[] 1} {ok map[success:Joined successfully]}}"
//Supposed to get
{event: "spr_reply", topic: "sketchpad", ref: "45", payload: {status:
"ok", response: {}}}
On line 55 is where I get back the "corrupted" data - https://play.golang.org/p/TOzJuvewlP
Redigo supports the following conversions to Redis bulk strings:
Go Type Conversion
[]byte Sent as is
string Sent as is
int, int64 strconv.FormatInt(v)
float64 strconv.FormatFloat(v, 'g', -1, 64)
bool true -> "1", false -> "0"
nil ""
all other types fmt.Print(v)
The Reply type is encoding using fmt.Print(v).
It looks like you want to encode the value as JSON. If so, do the encoding in the application. You can remove the redis field tags.
writeToRedis(conn redis.Conn, data Reply) error {
p, err := json.Marshl(data)
if err != nil {
return errors.Wrap(err, "Unable to encode message to json")
}
if err := conn.Send("PUBLISH", Channel, p); err != nil {
return errors.Wrap(err, "Unable to publish message to Redis")
}
if err := conn.Flush(); err != nil {
return errors.Wrap(err, "Unable to flush published message to Redis")
}
return nil
}