I'm trying to learn type assertion and conversion. It's kinda complicated for me.
I have this example: (I'm using gin framework)
type Env struct {
db *sql.DB
}
func main() {
r := gin.Default()
// Initiate session management (cookie-based)
store := sessions.NewCookieStore([]byte("secret"))
r.Use(sessions.Sessions("mysession", store))
db, _ := sql.Open("sqlite3", "./libreread.db")
defer db.Close()
env := &Env{db: db}
r.GET("/", env.GetHomePage)
}
func (e *Env) _GetUserId(email string) int64 {
rows, err := e.db.Query("SELECT `id` FROM `user` WHERE `email` = ?", email)
CheckError(err)
var userId int64
if rows.Next() {
err := rows.Scan(&userId)
CheckError(err)
}
rows.Close()
return userId
}
func (e *Env) GetHomePage(c *gin.Context) {
session := sessions.Default(c)
email := session.Get("email")
if email != nil {
name := c.Param("bookname")
userId := e._GetUserId(email) // I'm stuck here.
}
So, in the above code.. I'm setting db Env type and passing it to router functions. From there, I need to call another function. How to do that?
When I call e._GetUserId(email), it says
cannot convert email (type interface {}) to type Env: need type assertion
How to solve this problem?. Do I need to use inferface{} instead of struct for Env type?
Drafting answer based on conversation from my comments.
Method session.Get("email") returns interface{} type.
And method e._GetUserId() accepts string parameter, so you need to do type assertion as string like -
e._GetUserId(email.(string))
Related
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)
I'm trying to build a generic function which will parse input (in JSON) into a specified structure. The structure may vary at run-time, based on parameters which are passed to the function. I'm currently trying to achieve this by passing an object of the right type and using reflect.New() to create a new output object of the same type.
I'm then parsing the JSON into this object, and scanning the fields.
If I create the object and specify the type in code, everything works. If I pass an object and try to create a replica, I get an "invalid indirect" error a few steps down (see code).
import (
"fmt"
"reflect"
"encoding/json"
"strings"
)
type Test struct {
FirstName *string `json:"FirstName"`
LastName *string `json:"LastName"`
}
func genericParser(incomingData *strings.Reader, inputStructure interface{}) (interface{}, error) {
//******* Use the line below and things work *******
//parsedInput := new(Test)
//******* Use vvv the line below and things don't work *******
parsedInput := reflect.New(reflect.TypeOf(inputStructure))
decoder := json.NewDecoder(incomingData)
err := decoder.Decode(&parsedInput)
if err != nil {
//parsing error
return nil, err
}
//******* This is the line that generates the error "invalid indirect of parsedInput (type reflect.Value)" *******
contentValues := reflect.ValueOf(*parsedInput)
for i := 0; i < contentValues.NumField(); i++ {
//do stuff with each field
fmt.Printf("Field name was: %s\n", reflect.TypeOf(parsedInput).Elem().Field(i).Name)
}
return parsedInput, nil
}
func main() {
inputData := strings.NewReader("{\"FirstName\":\"John\", \"LastName\":\"Smith\"}")
exampleObject := new(Test)
processedData, err := genericParser(inputData, exampleObject)
if err != nil {
fmt.Println("Parsing error")
} else {
fmt.Printf("Success: %v", processedData)
}
}
If I can't create a replica of the object, then a way of updating / returning the one supplied would be feasible. The key thing is that this function must be completely agnostic to the different structures available.
reflect.New isn't a direct analog to new, as it can't return a specific type, it only can return a reflect.Value. This means that you are attempting to unmarshal into a *reflect.Value, which obviously isn't going to work (even if it did, your code would have passed in **Type, which isn't what you want either).
Use parsedInput.Interface() to get the underlying value after creating the new value to unmarshal into. You then don't need to reflect on the same value a second time, as that would be a reflect.Value of a reflect.Value, which again isn't going to do anything useful.
Finally, you need to use parsedInput.Interface() before you return, otherwise you are returning the reflect.Value rather than the value of the input type.
For example:
func genericParser(incomingData io.Reader, inputStructure interface{}) (interface{}, error) {
parsedInput := reflect.New(reflect.TypeOf(inputStructure).Elem())
decoder := json.NewDecoder(incomingData)
err := decoder.Decode(parsedInput.Interface())
if err != nil {
return nil, err
}
for i := 0; i < parsedInput.Elem().NumField(); i++ {
fmt.Printf("Field name was: %s\n", parsedInput.Type().Elem().Field(i).Name)
}
return parsedInput.Interface(), nil
}
https://play.golang.org/p/CzDrj6sgQNt
I am faily new to Go and I am trying to create a structured application using guidance from Ben Johnson's webpage. Unfortunately, his example is not a complete working application.
His webpage is https://medium.com/#benbjohnson/standard-package-layout-7cdbc8391fc1
I have tried to use his methods and I keep getting "Undefined: db" error. It doesn't tell me what line is causing the error, just the file "MSSQL.go"
Could someone help with guidance to help me fix this error?
Edited code with accepted solution.
StatementPrinter.go
package statementprinter
type Statement struct {
CustomerId string
CustomerName string
}
type StatementService interface {
Statement(id string) (*Statement, error)
}
main.go
package main
import (
"fmt"
"log"
"github.com/ybenjolin/StatementPrinter"
"github.com/ybenjolin/StatementPrinter/mssql"
"database/sql"
_ "github.com/alexbrainman/odbc"
)
const DB_INFO = "Driver={SQL Server};Server=cdc-edb2;Database=CostarReports;Trusted_Connection=yes;"
var db *sql.DB
func init() {
var err error
db, err = sql.Open("odbc", DB_INFO)
if err != nil {
log.Fatal("Error opening database connection.\n", err.Error())
}
err = db.Ping()
if err != nil {
log.Fatal("Error pinging database server.\n", err.Error())
}
fmt.Println("Database connection established.")
}
func main () {
var err error
defer db.Close()
// Create services
// Changes required here. Was ss := &statementprinter.Stat..
ss := &mssql.StatementService{DB: db}
// Use service
var s *statementprinter.Statement
s, err = ss.Statement("101583")
if err != nil {
log.Fatal("Query failed:", err.Error())
}
fmt.Printf("Statement: %+v\n", s)
}
mssql.go
package mssql
import (
_ "github.com/alexbrainman/odbc"
"database/sql"
"github.com/ybenjolin/StatementPrinter"
)
// StatementService represents a MSSQL implementation of statemenetprinter.StatementService.
type StatementService struct {
DB *sql.DB
}
// Statement returns a statement for a given customer.
func (s *StatementService) Statement(customer string) (*statementprinter.Statement, error) {
var err error
var t statementprinter.Statement
// Changes required here. Was row := db.Query......
row := s.DB.QueryRow(`Select Customer, CustomerName From AccountsReceivable.rptfARStatementHeader(?)`, customer)
if row.Scan(&t.CustomerId, &t.CustomerName); err != nil {
return nil, err
}
return &t, nil
This seems like it's just a typo. It seems like the problematic line is in the method
func (s *StatementService) Statement(customer string)
in mssql.go,
row := db.QueryRow(`Select Customer, CustomerName From AccountsReceivable.rptfARStatementHeader(?)`, customer)
QueryRow is supposed to be a method of db, but db is not defined. However, in the struct
type StatementService struct {
DB *sql.DB
}
there's a *sql.DB instance. The method you're using has a *StatementService parameter, s. So, my guess is the intention would be to access the sql.DB field in s like so
func (s *StatementService) Statement(customer string) (*statementprinter.Statement, error) {
var err error
var t statementprinter.Statement
//CHANGED LINE:
row := s.DB.QueryRow(`Select Customer, CustomerName From AccountsReceivable.rptfARStatementHeader(?)`, customer)
if row.Scan(&t.CustomerId, &t.CustomerName); err != nil {
return nil, err
}
return &t, nil
Then, the method is called in main.go, and is passed a StatementService instance that contains a database:
ss := &statementprinter.StatementService{DB: db}
I believe you need to change this line to
ss := &mssql.StatementService{DB: db}
becuase that's the actual interface implementation. The line you have now treats the StatementService interface like a struct which will not compile.
The global db in main.go lives for the lifetime of the application. It's just a pointer which is copied around for use.
My goal is using a struct to wrap sql.DB to do something more than sql.DB.
The instance I create can't work and give me this error when I run it.
DbConn.db.prepare undefined (cannot refer to unexported field or method sql.(*DB)."".prepare)
My code is:
type DatabaseConn struct {
driverName string
databaseName string
userName string
password string
dataSourceName string
db *sql.DB
}
func (d DatabaseConn)Open() error {
d.driverName = DB_DRNAME
d.userName = DB_UNAME
d.password = DB_PWD
d.databaseName = DB_DBNAME
d.dataSourceName = fmt.Sprintf("%s:%s#/%s?charset=utf8",d.userName, d.password, d.databaseName)
db, err := sql.Open(d.driverName, d.dataSourceName)
return err
}
func (d *DatabaseConn)Close() error {
defer func() {
if err := recover(); err != nil {
fmt.Println("Trying to handle error in DatabaseConn.Close(): ", err)
}
}()
err := d.db.Close()
return err
}
I am trying to create an instance and invoke sql.DB method.
var dbConn DatabaseConn
dbConn.Open()
defer dbConn.Close()
dbQuery := fmt.Sprintf("SELECT *, FROM ms_node WHERE node_id = ?")
getNodeRecord, err := dbConn.db.prepare(dbQuery)
The error message is pointed here.
./server.go:343: dbConn.db.prepare undefined (cannot refer to unexported field or method sql.(*DB)."".prepare)
(Personal background: I am newbie for 2 weeks)
The function Prepare starts with a capital letter ;)
So you should have:
getNodeRecord, err := dbConn.db.Prepare(dbQuery)
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
}