I'm using Go Buffalo's ORM Pop and want to store JSON in a field and be able to marshal it into a struct.
e.g.
schema.sql
CREATE TABLE tree (
id uuid PRIMARY KEY,
name text NOT NULL,
fruit json,
);
main.go
type Tree struct {
ID uuid.UUID `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Fruit []Fruit `json:"fruit" db:"fruit"`
}
type Fruit struct {
ID int `json:"id"`
Name string `json:"name"`
}
I get the following error:
sql: converting argument $25 type: unsupported type []Fruit, a slice of struct
UPDATE
Based on feedback added the following methods:
type Fruits []Fruit
// Value implements the driver.Valuer interface
func (f Fruits) Value() (driver.Value, error) {
return json.Marshal(of)
}
// Scan implements the sql.Scanner interface
func (f * Fruits) Scan(value interface{}) error {
var data []byte
if b, ok := value.([]byte); !ok {
data = b
return errors.New("type assertion to []byte for Fruit failed")
}
return json.Unmarshal(data, &of)
}
Now receive the error:
Cannot fetch from database: unable to fetch records: sql: Scan error on column index 26, name "fruit": unexpected end of JSON input
Update 2
Updated Scan method to the following and fixed error:
// Scan implements the sql.Scanner interface
func (f * Fruits) Scan(value interface{}) error {
var data = []byte(value.([]uint8))
return json.Unmarshal(data, &of)
}
Based on the help provided by #mkopriva
You need to provide Value and Scan methods for your struct
// Tree tall tree with many fruit
type Tree struct {
ID uuid.UUID `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Fruit []Fruit `json:"fruit" db:"fruit"`
}
// Fruit fruit found on a tree
type Fruit struct {
ID int `json:"id"`
Name string `json:"name"`
}
// Creates a splice of Fruit
type Fruits []Fruit
// Value implements the driver.Valuer interface
func (f Fruits) Value() (driver.Value, error) {
return json.Marshal(of)
}
// Scan implements the sql.Scanner interface
func (f * Fruits) Scan(value interface{}) error {
var data = []byte(value.([]uint8))
return json.Unmarshal(data, &f)
}
Related
I need to store an object that implements a specific type in an in-memory store. The type of the object being stored must satisfy an interface which is a superset of the interface implemented by the stored object.
What I'm trying to figure out is how do I access the original [interface] type when I retrieve the object from the store? The type switch from superset to subset does not seem to be possible.
Here is a sample code that will hopefully make this easier to understand.
Interface types
type Object interface {
UID() string
Name() string
Attrs() map[string]string
Parent() string
}
type Entity interface {
UID() string
Name() string
Attrs() map[string]string
}
type Node interface {
Entity
}
type SpecialNode interface {
Node
ID() int
}
You can see that Object interface is a subset of Entity interface i.e. all Objects automatically implement Entity interface, hence Entity is a superset of Object.
Sample implementations of the interfaces:
type node struct {
Node
}
type specialNode struct {
Node
id int
}
func (n specialNode) ID() int {
return n.id
}
type object struct {
uid string
name string
attrs map[string]string
parent string
}
func (e object) UID() string { return e.uid }
func (e object) Name() string { return e.name }
func (e object) Attrs() map[string]string { return e.attrs }
func (e object) Parent() string { return e.parent }
Now, here is a sample main.go:
package main
import "fmt"
func main() {
o := object{
uid: "xyz",
name: "entName",
attrs: make(map[string]string),
parent: "entParent",
}
n := node{
Node: o,
}
sn := specialNode{
Node: n,
id: 100,
}
fmt.Printf("%#v\n", sn)
}
This prints the following as expected:
main.specialNode{Node:main.node{Node:main.object{uid:"xyz", name:"entName", attrs:map[string]string{}, parent:"entParent"}}, id:100}
Now, how do I "extract" object/Object from sn? Type switching like this sn.Node.(Object) is not possible. Is there any way to accomplish this in Go?
Link to playground
You have to cast it to an interface to type cast
See Cannot type switch on non-interface value
o2:= interface{}(sn.Node.(node).Entity.(object))
var oo2 Object = o2.(Object)
fmt.Printf("%#v\n",oo2)
I have a bunch of preloads to preload nested relations
func Companies(db *database.Database) fiber.Handler {
return func(c *fiber.Ctx) error {
companies := new([]model.Company)
result := db.Preload("Relation.Addresses").
Preload("Relation.Contacts").
Preload("Relation.People").
Preload("Relation.BankAccounts").
Preload(clause.Associations).
Find(&companies)
if result.Error != nil {
return c.JSON(responseKit.RecordNotFoundError())
}
return c.JSON(responseKit.RecordsFoundSuccess(*companies, len(*companies)))
}
}
These are my structs
type Company struct {
PrivateGormModel
Name string `json:"name"`
Relation Relation `gorm:"polymorphic:Owner;" json:"relation"`
}
type Relation struct {
PrivateGormModel
OwnerID uint `json:"ownerID"`
OwnerType string `json:"ownerType"`
Addresses []Address `gorm:"polymorphic:Owner;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"addresses"`
Contacts []Contact `gorm:"polymorphic:Owner;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"contacts"`
People []Person `gorm:"polymorphic:Owner;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"people"`
BankAccounts []BankAccount `gorm:"polymorphic:Owner;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"bankAccounts"`
}
Which I found here and works
https://gorm.io/docs/preload.html
But I think this code is error prone because I pass the field names as a string and the field names might change in the future. Is there a way around this?
You can do this with reflection package. See this part of code:
package main
import (
"fmt"
"reflect"
)
type StructA struct {}
type StructB struct {}
type StructC struct {}
func GetTypeName(data interface{}) string {
if data == nil {
return ""
}
refVal := reflect.TypeOf(data)
if refVal.Kind() != reflect.Struct {
return ""
}
return refVal.Name();
}
func main() {
fmt.Printf("%T\n", StructA{})
fmt.Printf("%#v\n", StructB{})
fmt.Println(GetTypeName(StructC{}))
fmt.Println(GetTypeName(StructB{}))
fmt.Println(GetTypeName(StructA{}))
fmt.Println(GetTypeName("ABC"))
fmt.Println(GetTypeName(nil))
}
It produces the following output:
main.StructA
main.StructB{}
StructC
StructB
StructA
It should help you be compile-time safer, but you need to remember it is a little slower
I am trying to update "age" of data struct using SetAge() function after the array creation in the user struct. Here is the code snippet:
//data struct to set the user details
type data struct {
Name string `json:"name"`
College string `json:"college"`
Age int64 `json:"age"`
}
// user struct to store the user details in JSON Array
type user struct {
DataValue []*data `json:"data"`
}
func (u *user) Details(name, college string) *user {
d:=&data{Name:name, College:college}
u.DataValue=append(u.DataValue, d)
return u
}
func (u *user) SetAge(age int64) *user { //age is optional
// what code should be here such that age is added to resp detail
}
Output:
"data":[{
"name":"test",
"college":"test",
"age":10
},{
"name":"test",
"college":"test"
// in this object "Age" hasn't been set
}]
If you want to update the Age field of all the data objects, You're almost done.
You just need to iterate over the u.DataValue slice, and update the age field as follows:
func (u *user) SetAge(age int64) *user {
for index := range u.DataValue {
u.DataValue[index].Age = age
}
return u
}
As per the requirement in my application, it would be like this:
func (u *user) SetAge(age int64) *user {
u.DataValue[len(u.DataValue) - 1].Age = age
return u
}
Here is the struct in models_gen.go
type Students struct {
StudentID string `json:"student_id"`
Class string `json:"class"`
TestsHistory []*TestsHistory `json:"test_history"
}
type TestsHistory struct {
English string `json:"eng"`
Math string `json:"math"`
Music string `json:"music"`
PE string `json:"pe"`
}
The query function in resolvers.go:
func (r *queryResolver) GetStudents(ctx context.Context, id *int) (*model.Students, error) {
var students []*model.students
engine.Sync2(new(model.students))
fmt.Printf("%v\n", engine.Find(students))
return nil, nil
}
When I check MySQL, the students table only contains "StudentID" and "Class" Field, no "TestsHistory". How do I get "TestsHistory" in resolvers?
I want to unmarshal several types from JSON and use the interface to represent the actual struct that it is different. But when I send the struct as interface{} it converts it to a map. The animal.json is:
"{"type":"cat","policies":[{"name":"kitty","parameter":{"duration":600,"percent":90}}]}"
package main
import (
"reflect"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
)
func main() {
var err error
animal := New()
viper.SetConfigType("json")
viper.SetConfigName("animal")
viper.AddConfigPath("~/Desktop/")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
return
}
if err = viper.Unmarshal(&animal); err != nil {
return
}
for _, policy := range animal.Policies {
log.Info(policy.Name)
log.Info(policy.Parameter)
//INFO[0000] map[duration:600 percent:90]
log.Info(reflect.TypeOf(policy.Parameter))
//INFO[0000] map[string]interface {}, Why is it not an interface{} and how do I get it?
switch t := policy.Parameter.(type) {
//why does the switch not work?
case *CatParameter:
log.Info("cat", t)
case *DogParameter:
log.Info("dog", t)
}
}
}
func New() *Animal {
var animal Animal
animal.Type = "cat"
return &animal
}
type Animal struct {
Type string `json:"type" form:"type"`
Policies []Policy `json:"policies" form:"policies"`
}
type CatParameter struct {
Duration int `json:"duration"`
Percent int `json:"percent"`
}
type DogParameter struct {
Percent int `json:"percent"`
Duration int `json:"duration"`
Operation string `json:"operation"`
}
type Policy struct {
Name string `json:"name"`
Parameter interface{} `json:"parameter"`
}
It's json unmarshal feature
If you use an interface{} as a decoder, the default json object for interface{} is map[string]interface{}
You can see it here:
https://godoc.org/encoding/json#Unmarshal
bool, for JSON booleans
float64, for JSON numbers
string, for JSON strings
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null
So in t := policy.Parameter.(type), the t is map[string]interface{}
For solving your problem, you can try to define another field to distinguish CatParameter or DogParameter
Maybe:
type Policy struct {
Name string `json:"name"`
Parameter Parameter `json:"parameter"`
}
type Parameter struct {
Name string `json:"name"` // cat or dog
Percent int `json:"percent,omitempty"`
Duration int `json:"duration,omitempty"`
Operation string `json:"operation,omitempty"`
}