golang simplejson mustint64 doesn't convert from string to int64 - go

I am using simplejson, and it provides type asserters.
fmt.Printf("%s %s", m.Get("created_time").MustString(), m.Get("created_time").MustInt64())
above code shows this result:
1506259900 %!s(int64=0)
So MustInt64() gives 0 instead of converted Int64 value.
Is it because the 1506259900 is too big to be converted?
Thank for your help!

The original json was:
{"created_time":"1505733738"}
Not
{"created_time":1505733738}
It's originally a STRING, not a NUMBER.
So, when use MustInt64() to that json, it should return 0 because type is not matched.
Right way to treat this is using strconv.
i64, err := strconv.ParseInt(m.Get("created_time").MustString(), 10, 64)
And you'll get what you wanted as i64.

Related

How to output json format errors in Golang rest, specifically referencing the bad fields

I have the following requirement: return errors from a REST API in the following format:
Error format
422
{
"name-of-field": [
"can't be blank",
"is too silly"
]
}
My code looks like this:
var PostFeedback = func(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
surveyId := params["id"]
feedback := &models.Feedback{}
err := json.NewDecoder(r.Body).Decode(feedback)
if err != nil {
jsonError := fmt.Sprintf(`{
"%s": [
"%s"
]
}`, "errors", err)
log.Printf("invalid input format, %v", jsonError)
resp := map[string]interface{}{"error": jsonError}
u.Respond(w, resp)
return
}
Questions:
How do I get the names of the offending fields?
How do I satisfy the requirement best?
The encoding/json package doesn't provide validation for "blank", nor "silly" values. It will return an error only if the data in the body is not a valid json, or if the field types in the json do not, according to the package's spec, match the field types of the structure into which you're trying to decode that json.
The 1st type of error would be the json.SyntaxError, if you get this it is not always possible to satisfy your requirements since there may be no actual fields which you could use in your response, or if there are json fields, they, and their values, may be perfectly valid json, but the cause of the error may lie elsewhere (see example).
In cases where the data holds actual json fields but it has, for example, non-json values you could use the Offset field of the SyntaxError type to find the closest preceding field in the data stream. Using strings.LastIndex you can implement a naive solution to look backwards for the field.
data := []byte(`{"foobar": i'm not json}`)
err := json.Unmarshal(data, &T{})
se, ok := err.(*json.SyntaxError)
if !ok {
panic(err)
}
field := string(data[:se.Offset])
if i := strings.LastIndex(field, `":`); i >= 0 {
field = field[:i]
if j := strings.LastIndex(field, `"`); j >= 0 {
field = field[j+1:]
}
}
fmt.Println(field) // outputs foobar
Playground link
NOTE: As you can see, for you to be able to look for the field, you need to have access to the data, but when you're using json.NewDecoder and passing it the request's body directly, without first storing its contents somewhere, you'll loose access to that data once the decoder's Decode method is done. This is because the body is a stream of bytes wrapped in a io.ReadCloser that does not support "rewinding", i.e. you cannot re-read bytes that the decoder already read. To avoid this you can use ioutil.ReadAll to read the full contents of the body and then json.Unmarshal to do the decoding.
The 2nd type of error would be the json.UnmarshalTypeError. If you look at the documentation of the error type and its fields you'll know that all you need to do is to type assert the returned value and you're done. Example
Validation against "blank" and "silly" values would be done after the json has been successfully decoded into your structure. How you do that is up to you. For example you could use a 3rd party package that's designed for validating structs, or you can implement an in-house solution yourself, etc. I actually don't have an opinion on which one of them is the "best" so I can't help you with that.
What I can say is that the most basic approach would be to simply look at each field of the structure and check if its value is valid or not according to the requirements for that field.

Solution for type assertion without generics with Golang?

I'm using gorm, and it allows many data types like int, uint, int8, uint8 ....
Then I have a plugin in template like this:
f["UNIX2STR"] = func(t interface{}, f string) string {
switch t.(type) {
case int:
return time.Unix(int64(t.(int)), 0).Format(f)
case uint:
return time.Unix(int64(t.(uint)), 0).Format(f)
case uint8:
return time.Unix(int64(t.(uint8)), 0).Format(f)
case *int:
return time.Unix(int64(*t.(*int)), 0).Format(f)
case *uint:
return time.Unix(int64(*t.(*uint)), 0).Format(f)
case *uint8:
return time.Unix(int64(*t.(*uint8)), 0).Format(f)
.....
default:
return ""
}
// return time.Unix(int64(t), 0).Format(f)
}
It converts all integer types to formatted string.
So what am I suppose to do? List all gorm supported int types and cast it to int64?
I have searched many days for solution convert interface{} to its true type without using type assertion but didn't work.
I've not used gorm, but I think that something like this could solve your problem:
func formatUnix(t interface{}, f string) (string, error) {
timestampStr := fmt.Sprint(t)
timestamp, err := strconv.ParseInt(timestampStr, 10, 64)
if err != nil {
return "", err
}
return time.Unix(timestamp, 0).Format(f), nil
}
Rather than listing all potential types, it simply converts the interface{} to a string using fmt.Sprint() and then convert the string to int64 using strconv.ParseInt().
Based on your comments, it sounds like you're concerned with converting any numeric type to a string. This is easily done with fmt.Sprint:
stringValue := fmt.Sprint(i) // i is any type
But this has nothing to do with GORM.
On the other hand, if your problem is that GORM is returning an unpredictable type, just change your select statement to always return a string. For example, for MySQL, something like:
SELECT CAST(someNumberColumn AS VARCHAR) AS stringColumn
or
SELECT CAST(someNumberColumn AS INT) AS intColumn
I do not think that this is a problem with go or gorm. I am a bit baffled that you save your unix timestamps in many different formats. BTW, A unix timestamp is 32 bit, so there is no point in converting (and saving in the first place) any 8 bit ints.
A solution would be to use a unified data type (int64) for all timestamps in your structs. After that your formatting func can accept int64 instead ofinterface{}, without the need of any type assertions.

How to cut uuid in golang?

In order to make semi-random slugs, I'd like to use first 8 characters of uuid. So I have
import (
fmt
"github.com/satori/go.uuid"
)
u1 := uuid.NewV4()
fmt.Println("u1 :", u1)
runes := []rune(u1)
slug := string(runes[0:7])
But in compile time I get this error:
cannot convert u1 (type uuid.UUID) to type []rune
How can I fix it?
There is no need to convert the UUID to a []rune. That UUID type is stored in a binary representation as a [16]byte. There is a UUID.String() method which you can use to convert to a string, then slice it.
slug := u1.String()[:7]
In that package (I just looked at the source code) a UUID is an alias for [16]byte, so you cannot concert it to a rune array, not that you want to.
Try this:
s := hex.EncodeToString(u1.Bytes()[:4])
This will give you 8 hex digits. However, this is still a roundabout way of doing things. A v4 UUID is random except for certain bits, so if you are not using the whole UUID it is more straightforward to just generate 4 random bytes. Use the Read() function in math/rand (which must be seeded) or crypto/rand (which is what the UUID library uses).
b := make([]byte, 4)
rand.Read(b) // Doesn’t actually fail
s := hex.EncodeToString(b)

Display base 10 representation of huge hexa string?

Trying to find how to have this hexa string "58068906d6194c6cbda7a6df" into it's base 10 representation.
I tried with this:
i, err := strconv.Parse("58068906d6194c6cbda7a6df", 16, 64)
Obviously I'm getting this error: parsing "58068906d6194c6cbda7a6df"; value out of range
I also need to take the base 10 string representation and get this hexa value back after some processing. i.e.:
base10 := "58068906d6194c6cbda7a6df" => to base 10 string
some processing
hexa := base10 => to base 16 string
Can I use the fmt package to dislpay the base 10? I know that displaying the hexa of a base 10 I could use %x, but what can I do with an existing string?
Thanks for your help, for a reason I cannot understand, I'm unable to find any way to do this.
Your hex value is larger than int64 can hold, so you have to use a big.Int
https://play.golang.org/p/OPPL43u6nB
i := new(big.Int)
i.SetString("58068906d6194c6cbda7a6df", 16)
fmt.Println(i)
To get a hex string representation from a big.Int, you can use the Text method:
hex := i.Text(16)

How do I convert a float in the format of xxxxxxxe+09 to an int in Go?

Given a number like 1.400126761e+09 in Golang, what can I use to cast it to an int? I tried using the strconv library to play around with it and convert it using FormatFloat but that function returns the same thing when I give it the 'e' flag. Any other functions/libraries that will handle this conversion to an int?
Just use int(). For example:
x := float32(3.1)
fmt.Println(int(x))
ParseFloat is not returning the same thing, it's returning a float64 or float32. After you use it, you can just convert to an int as usual:
s := "1.400126761e+09"
f, err := strconv.ParseFloat(s, 64)
if err == nil {
thisisanint := int(f)
fmt.Println(thisisanint)
} else {
fmt.Println(err)
}
Go Playground
I actually was not clear as the variable I was playing with employs the interface{} and simply needed a float64 type assertion before casting it like int(). Hope this helps!

Resources