I'm trying to do something to bring the SQL results dynamically structure, basically I want to run a function by passing the rows and the structure(to get the data type and make one) and return in interface array.
Anyone know how I can do?
I dont want pass the direct "User" struct as param.. that is not dynamically
type User struct{
Id_user int `json:"id_user"`
Name string `json:"name"`
Email string `json:"email"`
Username string `json: "username"`
}
func main() {
var user User
rows, _ := db.Query("SELECT id_user, name, email, username FROM users")
json.NewEncoder(w).Encode(StructRow(user, rows))
}
func StructRow(u interface{}, rows *sql.Rows)[]interface{}{
var data []interface{}
for rows.Next() {
//How i can create a "user" here, dynamically
//for Example
//var user reflect.TypeOf(u)
_ = rows.Scan(StrutForScan(&user)...)
data = append(data, user)
}
return data
}
func StrutForScan(u interface{}) []interface{} {
val := reflect.ValueOf(u).Elem()
v := make([]interface{}, val.NumField())
for i := 0; i < val.NumField(); i++ {
valueField := val.Field(i)
v[i] = valueField.Addr().Interface()
}
return v
}
Changing your function StructRow to
func StructRow(u interface{}, rows *sql.Rows) []interface{} {
var data []interface{}
for rows.Next() {
t := reflect.TypeOf(u)
val := reflect.New(t).Interface()
errScan := rows.Scan(StrutForScan(val)...)
if errScan != nil {
//proper err handling
}
data = append(data, val)
}
return data
}
will fix it. I guess.
For more on reflect package go to: https://golang.org/pkg/reflect/
Related
I'm using an AWS Lambda function written in Go with Dynamo stream but I don't see any way where I can marshall the old & new image to my struct because it's returning the image as map[string]DynamoDBAttributeValue.
I can check individual key and then assign it to my struct one by one, but is there an direct way to Marshall directly?
func HandleRequest(ctx context.Context, event events.DynamoDBEvent) {
for _, record := range event.Records {
var newStruct models.MyStruct // want to marshall newImage in this struct
logger.Debugf("New: %#v", record.Change.NewImage)
}
}
UPDATE:
Here is the custom DynamoDBEvent that I'm using now:
type DynamoDBEvent struct {
Records []DynamoDBEventRecord `json:"Records"`
}
type DynamoDBEventRecord struct {
AWSRegion string `json:"awsRegion"`
Change DynamoDBStreamRecord `json:"dynamodb"`
EventID string `json:"eventID"`
EventName string `json:"eventName"`
EventSource string `json:"eventSource"`
EventVersion string `json:"eventVersion"`
EventSourceArn string `json:"eventSourceARN"`
UserIdentity *events.DynamoDBUserIdentity `json:"userIdentity,omitempty"`
}
type DynamoDBStreamRecord struct {
ApproximateCreationDateTime events.SecondsEpochTime `json:"ApproximateCreationDateTime,omitempty"`
Keys map[string]*dynamodb.AttributeValue `json:"Keys,omitempty"`
NewImage map[string]*dynamodb.AttributeValue `json:"NewImage,omitempty"`
OldImage map[string]*dynamodb.AttributeValue `json:"OldImage,omitempty"`
SequenceNumber string `json:"SequenceNumber"`
SizeBytes int64 `json:"SizeBytes"`
StreamViewType string `json:"StreamViewType"`
}
Unfortunately there is no elegant way yet, sot you have to check each key/value pairs and assign them to your struct.
PS: As small sugar you can use func FromDynamoDBMap from this package and work with map[string]interface{} not with map[string]events.DynamoDBAttributeValue which is way easier.
I have created a solution for the problem. This was crucial to my implementation since my implementation uses custom keys.
Unfortunately, the Number type has to be cast to Float64 to make sure that it is not lost.
This will result in a completely usable struct against the changeset. You may need a map of your models and a known field
Usage
newResult, _ := UnmarshalStreamImage(record.Change.NewImage)
newMarshal, _ := attributevalue.MarshalMap(newResult)
model := SomeModel{}
err := attributevalue.UnmarshalMap(newMarshal, &model)
Function Code
// UnmarshalStreamImage converts events.DynamoDBAttributeValue to struct
func UnmarshalStreamImage(attribute map[string]events.DynamoDBAttributeValue) (map[string]interface{}, error) {
baseAttrMap := make(map[string]interface{})
for k, v := range attribute {
baseAttrMap[k] = extractVal(v)
}
return baseAttrMap, nil
}
func extractVal(v events.DynamoDBAttributeValue) interface{} {
var val interface{}
switch v.DataType() {
case events.DataTypeString:
val = v.String()
case events.DataTypeNumber:
val, _ = v.Float()
case events.DataTypeBinary:
val = v.Binary()
case events.DataTypeBoolean:
val = v.Boolean()
case events.DataTypeNull:
val = nil
case events.DataTypeList:
list := make([]interface{}, len(v.List()))
for _, item := range v.List() {
list = append(list, extractVal(item))
}
val = list
case events.DataTypeMap:
mapAttr := make(map[string]interface{}, len(v.Map()))
for k, v := range v.Map() {
mapAttr[k] = extractVal(v)
}
val = mapAttr
case events.DataTypeBinarySet:
set := make([][]byte, len(v.BinarySet()))
for _, item := range v.BinarySet() {
set = append(set, item)
}
val = set
case events.DataTypeNumberSet:
set := make([]string, len(v.NumberSet()))
for _, item := range v.NumberSet() {
set = append(set, item)
}
val = set
case events.DataTypeStringSet:
set := make([]string, len(v.StringSet()))
for _, item := range v.StringSet() {
set = append(set, item)
}
val = set
}
return val
}```
I'm trying to return a struct from one function to another for use with an API. The function is to reduce repeating my code in other places in my API that I am developing.
I'm getting the below error:
cannot use getProjectLocations(params) (type []ProjectLocation) as type []ProjectLocation in assignment
Code below:
func GetProject(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)["uuid"]
type ProjectLocation struct {
UUID string `json:"uuid"`
Location string `json:"location"`
Primary bool `json:"is_primary"`
}
type Project struct {
UUID string `json:"uuid"`
Owner null.String `json:"project_owner"`
Name string `json:"project_name"`
Locations []ProjectLocation `json:"locations"`
}
q := `SELECT
p.uuid,
p.project_owner,
p.project_name,
p.project_type,
p.project_status,
p.created_date,
p.created_by,
p.last_modified,
p.last_modified_by
FROM
projects p
WHERE p.uuid=$1 LIMIT 1;`
rows, err := global.DB.Query(q, params)
global.CheckDbErr(err)
var project Project
for rows.Next() {
err = rows.Scan(
&project.UUID,
&project.Owner,
&project.Name,
)
global.CheckDbErr(err)
}
project.Locations = getProjectLocations(params)
rows.Close()
json.NewEncoder(w).Encode(project)
}
func getProjectLocations(uuid string) []ProjectLocation {
var Locations []ProjectLocation
q := `SELECT uuid,location,is_primary FROM project_locations WHERE project_uuid=$1`
rows, err := global.DB.Query(q, uuid)
global.CheckDbErr(err)
for rows.Next() {
var location ProjectLocation
err = rows.Scan(
&location.UUID,
&location.Location,
&location.Primary,
)
Locations = append(Locations, location)
}
return Locations
}
I believe I'm returning a []ProjectLocation struct to the get project function, so I'm just a little confused as a beginner.
func GetProject(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)["uuid"]
type ProjectLocation struct {
UUID string `json:"uuid"`
Location string `json:"location"`
Primary bool `json:"is_primary"`
}
// ...
}
func getProjectLocations(uuid string) []ProjectLocation {
// ...
}
type ProjectLocation is local to func GetProject.
I defined a struct named Student and a map named score.
Data structure is shown below:
type Student struct {
CountryID int
RegionID int
Name string
}
stu := Student{111, 222, "Tom"}
score := make(map[Student]int64)
score[stu] = 100
i am using json.Marshal to marshal score into json, but i cannot use json.Unmarshal to unmarshal this json. Below is my code. i am using function GetMarshableObject to translate struct Student into string which is marshable.
Could anyone tell me how to deal with this json to unmarshal it back to map score.
package main
import (
"encoding/json"
"fmt"
"os"
"reflect"
)
type Student struct {
CountryID int
RegionID int
Name string
}
func GetMarshableObject(src interface{}) interface{} {
t := reflect.TypeOf(src)
v := reflect.ValueOf(src)
kind := t.Kind()
var result reflect.Value
switch kind {
case reflect.Map:
//Find the map layer count
layer := 0
cur := t.Elem()
for reflect.Map == cur.Kind() {
layer++
cur = cur.Elem()
}
result = reflect.MakeMap(reflect.MapOf(reflect.TypeOf("a"), cur))
for layer > 0 {
result = reflect.MakeMap(reflect.MapOf(reflect.TypeOf("a"), result.Type()))
layer--
}
keys := v.MapKeys()
for _, k := range keys {
value := reflect.ValueOf(GetMarshableObject(v.MapIndex(k).Interface()))
if value.Type() != result.Type().Elem() {
result = reflect.MakeMap(reflect.MapOf(reflect.TypeOf("a"), value.Type()))
}
result.SetMapIndex(reflect.ValueOf(fmt.Sprintf("%v", k)), reflect.ValueOf(GetMarshableObject(v.MapIndex(k).Interface())))
}
default:
result = v
}
return result.Interface()
}
func main() {
stu := Student{111, 222, "Tom"}
score := make(map[Student]int64)
score[stu] = 100
b, err := json.Marshal(GetMarshableObject(score))
if err != nil {
fmt.Println("error:", err)
}
os.Stdout.Write(b) //{"{111 222 Tom}":100}
scoreBak := make(map[Student]int64)
if err = json.Unmarshal(b, &scoreBak); nil != err {
fmt.Println("error: %v", err) // get error here: cannot unmarshal object into Go value of type map[main.Student]int64
}
}
From the docs:
The map's key type must either be a string, an integer type, or
implement encoding.TextMarshaler.
func (s Student) MarshalText() (text []byte, err error) {
type noMethod Student
return json.Marshal(noMethod(s))
}
func (s *Student) UnmarshalText(text []byte) error {
type noMethod Student
return json.Unmarshal(text, (*noMethod)(s))
}
As an example I'm using encoding/json to turn a Student value into a json object key, however that is not required and you can choose your own format.
https://play.golang.org/p/4BgZn4Y37Ww
I want to pass a struct object to a function & be able to access its pointer value from that function. I am not able to understand why the following is resulting in error.
func GetStructFieldPointers(u interface{}, jsonFields []string) []interface{} {
structVal := reflect.ValueOf(&u).Elem()
structType := reflect.TypeOf(u)
numberOfFields := structVal.NumField() // getting error here reflect:
// call of reflect.Value.NumField
// on interface Value
numberOfJSONFields := len(jsonFields)
res := make([]interface{}, numberOfJSONFields)
fmt.Println(jsonFields)
for fieldIndex, field := range jsonFields {
for i := 0; i < numberOfFields; i++ {
if structType.Field(i).Tag.Get("json") == field {
valueField := structVal.Field(i)
res[fieldIndex] = valueField.Addr().Interface()
}
}
}
return res
}
type User struct {
Id int `json:"id"`
Name string `json:"name"`
Address string `json:"address"`
}
user := User{}
res := GetStructFieldPointers(user, []string{"id", "name"})
To make this work, I had to make structType as a parameter like following:
func GetStructFieldPointers(u interface{}, structType reflect.Type, jsonFields []string) []interface{} {
structVal := reflect.ValueOf(u).Elem()
// structType := reflect.TypeOf(u)
numberOfFields := structVal.NumField()
numberOfJSONFields := len(jsonFields)
res := make([]interface{}, numberOfJSONFields)
fmt.Println(jsonFields)
for fieldIndex, field := range jsonFields {
for i := 0; i < numberOfFields; i++ {
if structType.Field(i).Tag.Get("json") == field {
valueField := structVal.Field(i)
res[fieldIndex] = valueField.Addr().Interface()
}
}
}
return res
}
user := User{}
res := GetStructFieldPointers(&user, reflect.TypeOf(user), []string{"id", "name"})
I like to know how to pass User{} as a parameter & use in both reflect.ValueOf & reflect.TypeOf calls.
On this line: structVal := reflect.ValueOf(&u).Elem() you're taking the address of an interface (the argument of your func) and not an address to the interface's underlying value, and then you're passing the pointer to ValueOf, so the .Elem() call returns the "elem value" to which the pointer points to, which is the interface, not struct.
If you know for a fact that the passed in value is a struct and not a pointer, all you need is this: structVal := reflect.ValueOf(u).
If a pointer was passed to your func, eg GetStructFieldPointers(&u, ... then this is what you want: structVal := reflect.ValueOf(u).Elem().
But also you can handle both cases by checking the value's kind.
rv := reflect.ValueOf(u)
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
if rv.Kind() == reflect.Struct {
fmt.Println(rv.NumField())
}
https://play.golang.org/p/9F9LNnwEaH
Update:
Took a better look at your code... If you want to be able to get the addresses of your struct's fields, you need to pass a pointer to the struct as the argument, or else those fields are going to be unaddressable.
https://play.golang.org/p/RaA2rau3s-
If one is working with a database, a Null* type is useful for most scenarios as one typically does not want a "zero" value going through, you want the NOT NULL constraints etc. to kick up and remind you that you haven't passed in all the data necessary.
So you create a structure like the following:
type Role struct {
Id sql.NullInt64
Code sql.NullString
}
Thats great, but now you cannot get direct access to the properties and have to use Role.Id.Value to both get and set, this is going to get pretty old in a large app when you have to have the extra step every single time you want access to the properties.
It would be nice if you could assign directly eg. Role.Code = "Fsfs", and be able to do something like Role.Code.IsNull when you are interested in null checking. Is such a thing possible?
Is using intermediate pointer value(s) an option?
package main
import "fmt"
type tmp struct {
Value int
}
func getInt() *int {
i := 123
return &i
}
func main() {
// Re
var v *int
v = nil
fmt.Printf("%T / %v\n", v, v)
if v == nil {
println("nil...")
}
v = getInt()
fmt.Printf("%T / %v\n", v, *v)
if v != nil {
println("not nil...")
}
s := tmp{*v}
fmt.Printf("%T / %v\n", s, s)
}
http://play.golang.org/p/lBrwTKh6-v
You can access Role.Code like that:
var r *Role
r.Code = *code
You can check for null like this:
fmt.Println(r.Code, r.Code.Valid)
If you change the value of r.Code manually without using an sql.Scanner a Setter could be helpful:
func (r *Role) SetCode(code string) {
r.Code.String = code
r.Code.Valid = true
}
func main() {
var r *Role
r.SetCode("mi")
if r.Code.Valid {
fmt.Println(r.Code)
}
I tried this out here: https://play.golang.org/p/faxQUm-2lr
Keep app and database code separate.
// Role belongs to app code, no compromises.
type Role struct {
Id int64
Code string
}
Model the database.
// Database has tables with columns.
type Table struct {
Name string
Columns []string
}
var RoleTable = Table{
Name: "roles",
Columns: []string{
"id",
"code",
},
}
Write code once to convert between model and database row.
// Database package needs to make it work.
// Write a model to database row.
type Writer struct {
Role
}
func (w *Writer) Write() []interface{} {
return []interface{}{
w.Role.Id,
sql.NullString{
Valid: len(w.Role.Code) > 0,
String: w.Role.String,
},
}
}
// Read a database row into model.
type Reader struct {
Id int64
Code sql.NullString
}
func (r *Reader) Scan(row *sql.Row) error {
return row.Scan(
&r.Id,
&r.Code,
)
}
func (r *Reader) Read() Role {
return Role{
Id: r.Id,
Code: r.Code.String,
}
}
Your schema is decoupled from app model. You can flatten structures like user contact details when saving or loading.
// Nested struct in app code.
type User struct {
TwitterProfile struct {
Id string
ScreenName string
}
}
// Database row is normalized flat.
var UserTable = Table{
Name: "users",
Columns: []string{
"twitter_id",
"twitter_screen_name",
},
}
It's flexible. You can even scan join rows without intermediate structs.
type RowMux struct {
vs []interface{}
}
func (mux *RowMux) Scan(vs ...interface{}) error {
mux.vs = append(mux.vs, vs...)
return nil
}
func (mux *RowMux) Mux(row *sql.Row) error {
return row.Scan(mux.vs...)
}
// Scan join rows!
row := db.QueryRow(`
SELECT users.*, roles.*
FROM users
JOIN roles ON users.id = roles.user_id
WHERE users.twitter_id = "123"
`)
mux := &RowMux{}
userReader := &UserReader{}
userReader.Scan(mux)
roleReader := &RoleReader{}
roleReader.Scan(mux)
if err := mux.Mux(row); err != nil {
panic(err)
}
user := userReader.Read()
role := roleReader.Read()