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.
Related
I have a struct with a map of objects. I wish to store elements of that map in a separate table. I was able to get it working for one struct, FooEntry, using the AfterFind() and BeforeSave() hooks; the former is called after the array of links is loaded. It works great when loading a specific row of FooEntryDb.
However, the very same code does not work for a struct, FooParent that has an array of FooEntryDb. The problem is that when a row of FooParent is read in, the AfterFind() hook is called before the FooEntryDb's links are added.
My query and code are as follows
Query
var pdb api.FooParentDb
result := db.
Joins("FooEntriesDb").
Preload("FooEntriesDb.FooEntryLinks").
Find(&pdb, 104185244543150166)
Code
type FooParent struct {
FooParentPK uint64 `gorm:"primary_key;autoIncrement:false;column:foo_parent_pk;type:INT8;"`
Entries []*FooEntry `gorm:"-"`
}
type FooParentDb struct {
FooParent
FooEntriesDb []*FooEntryDb `gorm:"foreignKey:FooParentPK;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}
// TableName overrides the table name used by FooParentDb to `foo_parents`
func (FooParentDb) TableName() string {
return "foo_parents"
}
func (tdb *FooParentDb) AfterFind(tx *gorm.DB) (err error) {
if tx.Error == nil {
for _, edb := range tdb.FooEntriesDb {
edb.FooParentPK = tdb.FooParentPK
tdb.Entries = append(tdb.Entries, &edb.FooEntry)
}
}
return
}
func (tdb *FooParentDb) BeforeSave(tx *gorm.DB) (err error) {
for _, e := range tdb.Entries {
tdb.FooEntriesDb = append(tdb.FooEntriesDb, &FooEntryDb{FooEntry: *e, FooParentPK: tdb.FooParentPK})
}
return
}
// FooEntry holds one row from the foo_entries table
type FooEntry struct {
FooEntryPK uint64 `gorm:"primary_key;autoIncrement:false;column:foo_entry_pk;type:INT8;"`
Links map[string]int64 `gorm:"-"`
}
// FooEntryDb holds one row from the foo_entries table
type FooEntryDb struct {
FooEntry
FooParentPK uint64 `gorm:"association_foreignkey:FooParentPK;column:foo_parent_pk;type:INT8;"`
FooLinks []FooLink `gorm:"foreignKey:EntryPK;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}
// TableName overrides the table name used by FooEntryDb to `foo_entries`
func (FooEntryDb) TableName() string {
return "foo_entries"
}
func (edb *FooEntryDb) AfterFind(tx *gorm.DB) (err error) {
if tx.Error == nil {
edb.Links = make(map[string]int64)
for _, l := range edb.FooLinks {
if l.Key == "" {
return fmt.Errorf("empty key for entry links")
}
edb.Links[l.Key] = l.Link
}
}
return
}
func (edb *FooEntryDb) BeforeSave(tx *gorm.DB) (err error) {
var sorted []string
for key := range edb.Links {
sorted = append(sorted, key)
}
sort.Slice(sorted, func(i, j int) bool { return sorted[i] < sorted[j] })
edb.FooLinks = nil
for _, key := range sorted {
edb.FooLinks = append(edb.FooLinks, FooLink{Key: key, Link: edb.Links[key]})
}
return
}
// FooLink holds one row from the foo_entry_links table
type FooLink struct {
FooEntryPK uint64 `gorm:"primary_key;autoIncrement:false;association_foreignkey:FooEntryPK;column:foo_entry_pk;type:INT8;"`
Key string `gorm:"primary_key;column:foo_key;type:VARCHAR;size:64;"`
Link int64 `gorm:"column:foo_link;type:INT4;"`
}
// TableName overrides the table name used by FooLink to `foo_entry_links`
func (FooLink) TableName() string {
return "foo_entry_links"
}
I have the following:
package main
import (
"encoding/json"
"fmt"
"os"
"reflect"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
tag string `json:"-"`
Another
}
type Another struct {
Address string `json:"address"`
}
func (u *User) MarshalJSON() ([]byte, error) {
value := reflect.ValueOf(*u)
for i := 0; i < value.NumField(); i++ {
tag := value.Type().Field(i).Tag.Get("json")
field := value.Field(i)
fmt.Println(tag, field)
}
return json.Marshal(u)
}
func main() {
anoth := Another{"123 Jennings Street"}
_ = json.NewEncoder(os.Stdout).Encode(
&User{1, "Ken Jennings", "name",
anoth},
)
}
I am trying to json encode the struct but before I do I need to change the json key...eg the final json should look like:
{"id": 1, "name": "Ken Jennings", "address": "123 Jennings Street"}
I noticed the method for value.Type().Field(i).Tag.Get("json"), however there is no setter method. Why? and how do I get the desired json output.
Also, how do I iterate through all the fields, including the embedded struct Another?
https://play.golang.org/p/Qi8Jq_4W0t
Since Go 1.8 you can use this solution:
func main() {
anoth := Another{"123 Jennings Street"}
_ = json.NewEncoder(os.Stdout).Encode(
&User{1, "Ken Jennings", "name",
anoth},
)
}
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
tag string `json:"-"`
Another
}
type Another struct {
Address string `json:"address"`
}
func (u *User) MarshalJSON() ([]byte, error) {
type alias struct {
ID int64 `json:"id"`
Name string `json:"name"`
tag string `json:"-"`
Another
}
var a alias = alias(*u)
return json.Marshal(&a)
}
Will give us:
{"id":1,"name":"Ken Jennings","address":"123 Jennings Street"}
This solution made possible by the fact that in Go 1.8 you can assign structs with same structure but different tags to each other. As you see type alias has the same fields as type User but with different tags.
It's kludgy, but if you can wrap the struct in another, and use the new one for encoding, then you could:
Encode the original struct,
Decode it to an interface{} to get a map
Replace the map key
Then encode the map and return it
Thus:
type MyUser struct {
U User
}
func (u MyUser) MarshalJSON() ([]byte, error) {
// encode the original
m, _ := json.Marshal(u.U)
// decode it back to get a map
var a interface{}
json.Unmarshal(m, &a)
b := a.(map[string]interface{})
// Replace the map key
b["name"] = b["first"]
delete(b, "first")
// Return encoding of the map
return json.Marshal(b)
}
In the playground: https://play.golang.org/p/TabSga4i17
You can create a struct copy w/ new tag name by using reflect.StructOf and reflect.Value.Convert function
https://play.golang.org/p/zJ2GLreYpl0
func (u *User) MarshalJSON() ([]byte, error) {
value := reflect.ValueOf(*u)
t := value.Type()
sf := make([]reflect.StructField, 0)
for i := 0; i < t.NumField(); i++ {
fmt.Println(t.Field(i).Tag)
sf = append(sf, t.Field(i))
if t.Field(i).Name == "Name" {
sf[i].Tag = `json:"name"`
}
}
newType := reflect.StructOf(sf)
newValue := value.Convert(newType)
return json.Marshal(newValue.Interface())
}
It seems the tag property of type User in the question is meant to be used for renaming the JSON fieldname for the Name property.
An implementation of MarshalJSON with a bit of reflection can do the job without that additional tag property and also without an additional wrapper struct (as suggested in the accepted answer), like so:
package main
import (
"encoding/json"
"os"
"reflect"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
Another
}
type Another struct {
Address string `json:"address"`
}
// define the naming strategy
func (User) SetJSONname(jsonTag string) string {
if jsonTag == "first"{
return "name"
}
return jsonTag
}
// implement MarshalJSON for type User
func (u User) MarshalJSON() ([]byte, error) {
// specify the naming strategy here
return marshalJSON("SetJSONname", u)
}
// implement a general marshaler that takes a naming strategy
func marshalJSON(namingStrategy string, that interface{}) ([]byte, error) {
out := map[string]interface{}{}
t := reflect.TypeOf(that)
v := reflect.ValueOf(that)
fnctn := v.MethodByName(namingStrategy)
fname := func(params ...interface{}) string {
in := make([]reflect.Value, len(params))
for k, param := range params {
in[k] = reflect.ValueOf(param)
}
return fnctn.Call(in)[0].String()
}
outName := ""
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
switch n := f.Tag.Get("json"); n {
case "":
outName = f.Name
case "-":
outName = ""
default:
outName = fname(n)
}
if outName != "" {
out[outName] = v.Field(i).Interface()
}
}
return json.Marshal(out)
}
func main() {
anoth := Another{"123 Jennings Street"}
u := User{1, "Ken Jennings", anoth,}
e := json.NewEncoder(os.Stdout)
e.Encode(u)
}
This will print:
{"Another":{"address":"123 Jennings Street"},"id":1,"name":"Ken Jennings"}
However, be aware that MarshalJSON always sorts the JSON tags, while the standard encoder preserves the order of struct fields.
Here is my code:
package main
import (
"fmt"
"time"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
type ClientCustomer struct {
Id int `json:"Id"`
Name string
Created time.Time
key string
UserId int `gorm:"user_id"`
Modified time.Time
}
func (ClientCustomer) TableName() string {
return "Client_customer"
}
type ClientCustomerInvitation struct {
Id int
CustomerId int `gorm:"customer_id"`
CodeInvitationId int `gorm:"codeinvitation_id"`
}
func (ClientCustomerInvitation) TableName() string {
return "Client_customer_invitation"
}
func main() {
db, err := gorm.Open("sqlite3", "db.sqlite3?cache=shared&mode=rwc")
if err != nil {
panic("failed to connect database")
}
defer db.Close()
var clientCustomer ClientCustomer
rows, err := db.Model(&ClientCustomer{}).Rows()
defer rows.Close()
if err != nil {
panic(err)
}
var clientCustomerInvitation ClientCustomerInvitation
for rows.Next() {
db.ScanRows(rows, &clientCustomer)
db.First(&clientCustomerInvitation, "customer_id = ?", clientCustomer.Id)
fmt.Println(clientCustomer)
fmt.Println(clientCustomerInvitation)
}
}
but I'm not fond of this line:
db.First(&clientCustomerInvitation, "customer_id = ?", clientCustomer.Id)
Is there a way to call "customer_id" from the struct directly instead of using a string?
Ideally I would like to do something like:
db.First(&clientCustomerInvitation, ClientCustomerInvitation.CustomerId.gormAlias+" = ?", clientCustomer.Id)
I'm looking for a way to use the gorm alias for mapping the field in way that is more elegant and re usable than a mere string.
The only way to be able to get tag value from certain struct field, is by using reflect.
My suggestion, create a function that return tag value from specific struct field. Something like below:
func getGormAlias(obj interface{}, fieldName string) string {
if field, ok := reflect.TypeOf(obj).FieldByName(fieldName); ok {
return field.Tag.Get("gorm")
}
return ""
}
Then use it to get the tag value.
gormAliasCustomerId := getGormAlias(ClientCustomerInvitation{}, "CustomerId")
db.First(&clientCustomerInvitation, gormAliasCustomerId + " = ?", clientCustomer.Id)
Basically what getGormAlias() function does:
Use the reflect.Type on obj to get the reflect.Type value.
Then call .FieldByName() to get the reflect.Value object from selected field name.
The tag information is available through .Tag property. Use that to get the tag value of gorm.
I have the following:
package main
import (
"encoding/json"
"fmt"
"os"
"reflect"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
tag string `json:"-"`
Another
}
type Another struct {
Address string `json:"address"`
}
func (u *User) MarshalJSON() ([]byte, error) {
value := reflect.ValueOf(*u)
for i := 0; i < value.NumField(); i++ {
tag := value.Type().Field(i).Tag.Get("json")
field := value.Field(i)
fmt.Println(tag, field)
}
return json.Marshal(u)
}
func main() {
anoth := Another{"123 Jennings Street"}
_ = json.NewEncoder(os.Stdout).Encode(
&User{1, "Ken Jennings", "name",
anoth},
)
}
I am trying to json encode the struct but before I do I need to change the json key...eg the final json should look like:
{"id": 1, "name": "Ken Jennings", "address": "123 Jennings Street"}
I noticed the method for value.Type().Field(i).Tag.Get("json"), however there is no setter method. Why? and how do I get the desired json output.
Also, how do I iterate through all the fields, including the embedded struct Another?
https://play.golang.org/p/Qi8Jq_4W0t
Since Go 1.8 you can use this solution:
func main() {
anoth := Another{"123 Jennings Street"}
_ = json.NewEncoder(os.Stdout).Encode(
&User{1, "Ken Jennings", "name",
anoth},
)
}
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
tag string `json:"-"`
Another
}
type Another struct {
Address string `json:"address"`
}
func (u *User) MarshalJSON() ([]byte, error) {
type alias struct {
ID int64 `json:"id"`
Name string `json:"name"`
tag string `json:"-"`
Another
}
var a alias = alias(*u)
return json.Marshal(&a)
}
Will give us:
{"id":1,"name":"Ken Jennings","address":"123 Jennings Street"}
This solution made possible by the fact that in Go 1.8 you can assign structs with same structure but different tags to each other. As you see type alias has the same fields as type User but with different tags.
It's kludgy, but if you can wrap the struct in another, and use the new one for encoding, then you could:
Encode the original struct,
Decode it to an interface{} to get a map
Replace the map key
Then encode the map and return it
Thus:
type MyUser struct {
U User
}
func (u MyUser) MarshalJSON() ([]byte, error) {
// encode the original
m, _ := json.Marshal(u.U)
// decode it back to get a map
var a interface{}
json.Unmarshal(m, &a)
b := a.(map[string]interface{})
// Replace the map key
b["name"] = b["first"]
delete(b, "first")
// Return encoding of the map
return json.Marshal(b)
}
In the playground: https://play.golang.org/p/TabSga4i17
You can create a struct copy w/ new tag name by using reflect.StructOf and reflect.Value.Convert function
https://play.golang.org/p/zJ2GLreYpl0
func (u *User) MarshalJSON() ([]byte, error) {
value := reflect.ValueOf(*u)
t := value.Type()
sf := make([]reflect.StructField, 0)
for i := 0; i < t.NumField(); i++ {
fmt.Println(t.Field(i).Tag)
sf = append(sf, t.Field(i))
if t.Field(i).Name == "Name" {
sf[i].Tag = `json:"name"`
}
}
newType := reflect.StructOf(sf)
newValue := value.Convert(newType)
return json.Marshal(newValue.Interface())
}
It seems the tag property of type User in the question is meant to be used for renaming the JSON fieldname for the Name property.
An implementation of MarshalJSON with a bit of reflection can do the job without that additional tag property and also without an additional wrapper struct (as suggested in the accepted answer), like so:
package main
import (
"encoding/json"
"os"
"reflect"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
Another
}
type Another struct {
Address string `json:"address"`
}
// define the naming strategy
func (User) SetJSONname(jsonTag string) string {
if jsonTag == "first"{
return "name"
}
return jsonTag
}
// implement MarshalJSON for type User
func (u User) MarshalJSON() ([]byte, error) {
// specify the naming strategy here
return marshalJSON("SetJSONname", u)
}
// implement a general marshaler that takes a naming strategy
func marshalJSON(namingStrategy string, that interface{}) ([]byte, error) {
out := map[string]interface{}{}
t := reflect.TypeOf(that)
v := reflect.ValueOf(that)
fnctn := v.MethodByName(namingStrategy)
fname := func(params ...interface{}) string {
in := make([]reflect.Value, len(params))
for k, param := range params {
in[k] = reflect.ValueOf(param)
}
return fnctn.Call(in)[0].String()
}
outName := ""
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
switch n := f.Tag.Get("json"); n {
case "":
outName = f.Name
case "-":
outName = ""
default:
outName = fname(n)
}
if outName != "" {
out[outName] = v.Field(i).Interface()
}
}
return json.Marshal(out)
}
func main() {
anoth := Another{"123 Jennings Street"}
u := User{1, "Ken Jennings", anoth,}
e := json.NewEncoder(os.Stdout)
e.Encode(u)
}
This will print:
{"Another":{"address":"123 Jennings Street"},"id":1,"name":"Ken Jennings"}
However, be aware that MarshalJSON always sorts the JSON tags, while the standard encoder preserves the order of struct fields.
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/