Redigo ScanStruct error with time.Time - go

I am trying to read a struct that has a field of type time.Time using redigo's ScanStruct, which gives me the following error: cannot convert from Redis bulk string to time.Time.
Is the only way of fixing this to create my own time type that extends time.Time and implements RedisScan? That sounds bad as well...

Since Redis has no concept of time values it would make no sense for a generic driver such as redigo to perform some automatic conversion between the builin time.Time type and an arbitrary byte array. As such, it's up to the programmer to decide how to perform that conversion.
For example, supposing you have a "Person" type defined as such, including a created_at timestamp formatted as RFC3339 (a form of ISO 8601), you could define a custom "Timestamp" type with a "RedisScan" method as follows:
type Timestamp time.Time
type Person struct {
Id int `redis:"id"`
Name string `redis:"name"`
CreatedAt Timestamp `redis:"created_at"`
}
func (t *Timestamp) RedisScan(x interface{}) error {
bs, ok := x.([]byte)
if !ok {
return fmt.Errorf("expected []byte, got %T", x)
}
tt, err := time.Parse(time.RFC3339, string(bs))
if err != nil {
return err
}
*t = Timestamp(tt)
return nil
}
// ...
response, err := redis.Values(conn.Do("HGETALL", "person:1"))
if err != nil {
panic(err)
}
var p Person
err = redis.ScanStruct(response, &p)
if err != nil {
panic(err)
}
log.Printf("OK: p=%v", p)

Related

Error while trying to fetch queryresult.KV object in JSON.Unmarshal

I am a little bit confused here and although I have searched a lot on this, something is clearly missing from my knowledge and I am asking your help.
I have created a Hyperledger Fabric Network and installed a chaincode in it. And I want to make a function that retrieves all the World State inputs about the Keys. I have done it already with the bytes.Buffer and it worked. But what I want to do is to do it with a struct.
So, I created the following struct that has only the key:
type WSKeys struct {
Key string `json: "key"`
Namespace string `json: "Namespace"`
}
And this is my code function:
func (s *SmartContract) getAllWsDataStruct(APIstub shim.ChaincodeStubInterface , args []string) sc.Response {
var keyArrayStr []WSKeys
resultsIterator, err := APIstub.GetQueryResult("{\"selector\":{\"_id\":{\"$ne\": null }} }")
if err != nil {
return shim.Error("Error occured when trying to fetch data: "+err.Error())
}
for resultsIterator.HasNext() {
// Get the next record
queryResponse, err := resultsIterator.Next()
if err != nil {
return shim.Error(err.Error())
}
fmt.Println(queryResponse)
var qry_key_json WSKeys
json.Unmarshal([]byte(queryResponse), &qry_key_json)
keyArray = append(keyArray, qry_key_json)
}
defer resultsIterator.Close()
all_bytes, _ := json.Marshal(keyArray)
fmt.Println(keyArray)
return shim.Success(all_bytes)
}
When executing the above I get the following error:
cannot convert queryResponse (type *queryresult.KV) to type []byte
I can get the results correctly if I, for example do this:
func (s *SmartContract) getAllWsDataStruct(APIstub shim.ChaincodeStubInterface , args []string) sc.Response {
var keyArray []string
resultsIterator, err := APIstub.GetQueryResult("{\"selector\":{\"_id\":{\"$ne\": null }} }")
if err != nil {
return shim.Error("Error occured when trying to fetch data: "+err.Error())
}
for resultsIterator.HasNext() {
// Get the next record
queryResponse, err := resultsIterator.Next()
if err != nil {
return shim.Error(err.Error())
}
fmt.Println(queryResponse)
keyArray = append(keyArray, queryResponse.Key)
}
defer resultsIterator.Close()
all_bytes, _ := json.Marshal(keyArray)
fmt.Println(keyArray)
return shim.Success(all_bytes)
}
But, why I get the above error when trying to add the queryResponse into a custom struct?
Do I need to add it to a struct that is only its type?
Please someone can explain what I am missing here?
The error statement is verbose enough to indicate, that your []byte conversion failed for the type queryResponse which, with a bit of lookup seems to be a struct type. In Go you cannot natively convert a struct instance to its constituent bytes without encoding using gob or other means.
Perhaps your intention was to use the Key record in the struct for un-marshalling
json.Unmarshal([]byte(queryResponse.Key), &qry_key_json)

Problem parsing values of PostgreSQL TIMESTAMP type

In PostgreSQL, I have table called surveys.
CREATE TABLE SURVEYS(
SURVEY_ID UUID PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
SURVEY_NAME VARCHAR NOT NULL,
SURVEY_DESCRIPTION TEXT,
START_PERIOD TIMESTAMP,
END_PERIOD TIMESTAMP
);
As you can see only SURVEY_ID and SURVEY_NAME columns are NOT NULL.
In Go, I want to create new entry in that table by POST request. I send JSON object like this:
{
"survey_name": "NAME",
"survey_description": "DESCRIPTION",
"start_period": "2019-01-01 00:00:00",
"end_period": "2019-02-28 23:59:59"
}
Unfortunatly it raise strange ERROR:
parsing time ""2019-01-01 00:00:00"" as ""2006-01-02T15:04:05Z07:00"": cannot parse " 00:00:00"" as "T"
Where I make mistake and how to fix my problem?
models/surveys.go:
import (
"database/sql"
"time"
)
type NullTime struct {
time.Time
Valid bool
}
type Survey struct {
ID int `json:"survey_id"`
Name string `json:"survey_name"`
Description sql.NullString `json:"survey_description"`
StartPeriod NullTime `json:"start_period"`
EndPeriod NullTime `json:"end_period"`
}
controllers/surveys.go:
var CreateSurvey = func(responseWriter http.ResponseWriter, request *http.Request) {
// Initialize variables.
survey := models.Survey{}
var err error
// The decoder introduces its own buffering and may read data from argument beyond the JSON values requested.
err = json.NewDecoder(request.Body).Decode(&survey)
if err != nil {
log.Println(err)
utils.ResponseWithError(responseWriter, http.StatusInternalServerError, err.Error())
return
}
defer request.Body.Close()
// Execute INSERT SQL statement.
_, err = database.DB.Exec("INSERT INTO surveys (survey_name, survey_description, start_period, end_period) VALUES ($1, $2, $3, $4);", survey.Name, survey.Description, survey.StartPeriod, survey.EndPeriod)
// Shape the response depending on the result of the previous command.
if err != nil {
log.Println(err)
utils.ResponseWithError(responseWriter, http.StatusInternalServerError, err.Error())
return
}
utils.ResponseWithSuccess(responseWriter, http.StatusCreated, "The new entry successfully created.")
}
The error already says what is wrong:
parsing time ""2019-01-01 00:00:00"" as ""2006-01-02T15:04:05Z07:00"": cannot parse " 00:00:00"" as "T"
You are passing "2019-01-01 00:00:00" while it expects a different time format, namely RFC3339 (UnmarshalJSON's default).
To solve this, you either want to pass the time in the expected format "2019-01-01T00:00:00Z00:00" or define your own type CustomTime like this:
const timeFormat = "2006-01-02 15:04:05"
type CustomTime time.Time
func (ct *CustomTime) UnmarshalJSON(data []byte) error {
newTime, err := time.Parse(timeFormat, strings.Trim(string(data), "\""))
if err != nil {
return err
}
*ct = CustomTime(newTime)
return nil
}
func (ct *CustomTime) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("%q", time.Time(*ct).Format(timeFormat))), nil
}
Careful, you might also need to implement the Valuer and the Scanner interfaces for the time to be parsed in and out of the database, something like the following:
func (ct CustomTime) Value() (driver.Value, error) {
return time.Time(ct), nil
}
func (ct *CustomTime) Scan(src interface{}) error {
if val, ok := src.(time.Time); ok {
*ct = CustomTime(val)
} else {
return errors.New("time Scanner passed a non-time object")
}
return nil
}
Go Playground example.

How to parse non standard time format from json

lets say i have the following json
{
name: "John",
birth_date: "1996-10-07"
}
and i want to decode it into the following structure
type Person struct {
Name string `json:"name"`
BirthDate time.Time `json:"birth_date"`
}
like this
person := Person{}
decoder := json.NewDecoder(req.Body);
if err := decoder.Decode(&person); err != nil {
log.Println(err)
}
which gives me the error parsing time ""1996-10-07"" as ""2006-01-02T15:04:05Z07:00"": cannot parse """ as "T"
if i were to parse it manually i would do it like this
t, err := time.Parse("2006-01-02", "1996-10-07")
but when the time value is from a json string how do i get the decoder to parse it in the above format?
That's a case when you need to implement custom marshal and unmarshal functions.
UnmarshalJSON(b []byte) error { ... }
MarshalJSON() ([]byte, error) { ... }
By following the example in the Golang documentation of json package you get something like:
// First create a type alias
type JsonBirthDate time.Time
// Add that to your struct
type Person struct {
Name string `json:"name"`
BirthDate JsonBirthDate `json:"birth_date"`
}
// Implement Marshaler and Unmarshaler interface
func (j *JsonBirthDate) UnmarshalJSON(b []byte) error {
s := strings.Trim(string(b), "\"")
t, err := time.Parse("2006-01-02", s)
if err != nil {
return err
}
*j = JsonBirthDate(t)
return nil
}
func (j JsonBirthDate) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Time(j))
}
// Maybe a Format function for printing your date
func (j JsonBirthDate) Format(s string) string {
t := time.Time(j)
return t.Format(s)
}
If there are lots of struct and you just implement custom marshal und unmarshal functions, that's a lot of work to do so. You can use another lib instead,such as a json-iterator extension jsontime:
import "github.com/liamylian/jsontime"
var json = jsontime.ConfigWithCustomTimeFormat
type Book struct {
Id int `json:"id"`
UpdatedAt *time.Time `json:"updated_at" time_format:"sql_date" time_utc:"true"`
CreatedAt time.Time `json:"created_at" time_format:"sql_datetime" time_location:"UTC"`
}
I wrote a package for handling yyyy-MM-dd and yyyy-MM-ddThh:mm:ss dates at https://github.com/a-h/date
It uses the type alias approach in the answer above, then implements the MarshalJSON and UnmarshalJSON functions with a few alterations.
// MarshalJSON outputs JSON.
func (d YYYYMMDD) MarshalJSON() ([]byte, error) {
return []byte("\"" + time.Time(d).Format(formatStringYYYYMMDD) + "\""), nil
}
// UnmarshalJSON handles incoming JSON.
func (d *YYYYMMDD) UnmarshalJSON(b []byte) (err error) {
if err = checkJSONYYYYMMDD(string(b)); err != nil {
return
}
t, err := time.ParseInLocation(parseJSONYYYYMMDD, string(b), time.UTC)
if err != nil {
return
}
*d = YYYYMMDD(t)
return
}
It's important to parse in the correct timezone. My code assumes UTC, but you may wish to use the computer's timezone for some reason.
I also found that solutions which involved using the time.Parse function leaked Go's internal mechanisms as an error message which clients didn't find helpful, for example: cannot parse "sdfdf-01-01" as "2006". That's only useful if you know that the server is written in Go, and that 2006 is the example date format, so I put in more readable error messages.
I also implemented the Stringer interface so that it gets pretty printed in log or debug messages.

How to convert []interface{} to custom defined type - Go lang?

I am started working in Go. Having the following code
type Transaction struct{
Id string `bson:"_id,omitempty"`
TransId string
}
func GetTransactionID() (id interface{}, err error){
query := bson.M{}
transId, err := dbEngine.Find("transactionId", WalletDB, query)
//transId is []interface{} type
id, err1 := transId.(Transaction)
return transId, err
}
Find
package dbEngine
func Find(collectionName,dbName string, query interface{})(result []interface{}, err error){
collection := session.DB(dbName).C(collectionName)
err = collection.Find(query).All(&result)
return result, err
}
Problem
Error: invalid type assertion: transId.(string) (non-interface type []interface {} on left)
Any suggestion to change the []interface{} to Transaction type.
You can't convert a slice of interface{}s into any single struct. Are you sure you don't really want a slice of Transactions (i.e. a []Transaction type)? If so, you'll have to loop over it and convert each one:
for _, id := range transId {
id.(Transaction) // do something with this
}

Type conversion of string to int

Ok, so I've been having difficulties with the type conversion of a string to byte write. This is the compiler error:
cannot use row[5] (type uint8) as type string in function argument
cannot use &v (type *Field) as type int in function argument
This is an example of row[5]: $15,000.00
Ive declared a struct:
type Field struct {
Eia uint8
}
here is the main implementation:
for {
record, err := reader.Read()
if err == io.EOF {
break
} else if err != nil {
panic(err)
}
var v Field
for _, row := range record {
eia, err := strconv.ParseInt(row[5], 8, &v) // Estimated Incoming Amount
if err == nil {
fmt.Printf("%+v\n", v)
} else {
fmt.Println(err)
fmt.Printf("%+v\n", v)
}
Can anyone please explain to me how strconv can convert the row to a integer?
If you made a complete example on http://play.golang.org/ it'd be easier to give you a complete solution.
ParseInt() takes the string (you might have to do string(row[5])), the base (you probably meant 10) and the bitsize (that's where you should put 8).
It retuns an int (eia), it doesn't put it into the struct as it looks like you are trying.
Instead do if err == nil { v.Eia = eia }.

Resources