How to make parameter of function is a "generic" struct - go

For example, I want to write a method like this:
func parseData(rawData []json.RawMessage) []interface{} {
var migrations []interface{}
for _, migration := range rawData {
// this is an custom struct
command := UserCommand{}
json.Unmarshal(migration, &command)
migrations = append(migrations, command)
}
return migrations
}
The problem of this code is: If I don't want to parse UserCommand but any other such as ProductCommand, I must write the same code, only different at line: command := UserCommand{}. So my question is: how can I generic this code.
I have tried this solution but it doesn't work:
func parseData(rawData []json.RawMessage, class interface{}) []interface{} {
var migrations []interface{}
for _, migration := range rawData {
command := class
json.Unmarshal(migration, &command)
migrations = append(migrations, command)
}
return migrations
}
// then I call this method
parseData(data, UserCommand{})
But it doesn't work. It return array of map[string]interface{} How can I fix this.
Edit:
Here is some my defined struct
type UserCommand struct {
User string
Info string
}
type ProductCommand struct {
Name string
Quanlity int
}

// I want to be able to call this method
parseData(data, UserCommand{})
It is possible to support this style of "generic" signature by using Go's reflect package.
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type UserCommand struct {
User string
Info string
}
type ProductCommand struct {
Name string
Quantity int
}
func parseData(rawData []json.RawMessage, class interface{}) []interface{} {
var parsed []interface{}
for _, elem := range rawData {
// Create a pointer to a new zero value of the same type as `class`.
command := reflect.New(reflect.TypeOf(class))
// Pass a pointer to the new value to `json.Unmarshal`.
json.Unmarshal(elem, command.Interface())
// Insert the pointed-to new value into the output slice.
parsed = append(parsed, command.Elem().Interface())
}
return parsed
}
func main() {
data := []json.RawMessage{
json.RawMessage(`{"User":"u1","Info":"i1"}`),
json.RawMessage(`{"User":"u2","Info":"i2"}`),
}
parsed := parseData(data, UserCommand{})
fmt.Printf("%#v\n", parsed)
data = []json.RawMessage{
json.RawMessage(`{"Name":"n1","Quantity":1}`),
json.RawMessage(`{"Name":"n2","Quantity":2}`),
}
parsed = parseData(data, ProductCommand{})
fmt.Printf("%#v\n", parsed)
}
The output shows that the first parseData call has parsed two UserCommand structs and the second call has parsed two ProductCommand structs.
[]interface {}{main.UserCommand{User:"u1", Info:"i1"}, main.UserCommand{User:"u2", Info:"i2"}}
[]interface {}{main.ProductCommand{Name:"n1", Quantity:1}, main.ProductCommand{Name:"n2", Quantity:2}}

Related

How to convert string to struct array

I have a json array which is converted into a string. Now I want to map the string to a struct array so that I can modify the string json. Below is my code base
type ProcessdetailsEntity struct {
Source []int64 `json:"source"`
Node string `json:"node"`
Targets []int64 `json:"targets"`
Issave bool `json:"isSave"`
Instate []int64 `json:"inState"`
OutState []int64 `json:"outState"`
}
func main() {
var stringJson = "[{\"source\":[-1],\"node\":\"1_1628008588902\",\"targets\":[],\"isSave\":true,\"inState\":[1],\"outState\":[2]},{\"source\":[\"1_1628008588902\",\"5_1628008613446\"],\"node\":\"2_1628008595757\",\"targets\":[],\"isSave\":true,\"inState\":[2,5],\"outState\":[3,6]}]"
in := []byte(stringJson)
detailsEntity := []ProcessdetailsEntity{}
err := json.Unmarshal(in, &detailsEntity)
if err != nil {
log.Print(err)
}
}
Now when I run this code base I got the error:
json: cannot unmarshal string into Go struct field ProcessdetailsEntity.source of type int64
How to properly map the string to struct so that I can modify the inState and outState value of the json ?
The error you get is already pretty much on the nose:
cannot unmarshal string into Go struct field ProcessdetailsEntity.source of type int64
That tells you that (at least one) of your source fields appears to have the wrong type: a string instead of something that can be represented by a int64.
So let's check your source fields in your stringJson:
"source":[-1]
"source":["1_1628008588902","5_1628008613446"]
As you can see the second source is an array of string. Hence the error.
To solve this you need to make sure that the source is an array of int. Unfortunately, 1_1628008588902 and 5_1628008613446 are not valid integers in Go.
I slightly modified your JSON and fixed your code an then it works:
package main
import (
"encoding/json"
"log"
)
type ProcessdetailsEntity struct {
Source []int64 `json:"source"`
Node string `json:"node"`
Targets []int64 `json:"targets"`
Issave bool `json:"isSave"`
Instate []int64 `json:"inState"`
OutState []int64 `json:"outState"`
}
func main() {
var stringJson = `[
{
"source":[-1],
"node":"1_1628008588902",
"targets":[],
"isSave":true,
"inState":[1],
"outState":[2]
},
{
"source":[11628008588902,51628008613446],
"node":"2_1628008595757",
"targets":[],
"isSave":true,
"inState":[2,5],
"outState":[3,6]
}
]`
in := []byte(stringJson)
detailsEntity := []ProcessdetailsEntity{}
err := json.Unmarshal(in, &detailsEntity)
if err != nil {
log.Print(err)
}
}
See: https://play.golang.org/p/kcrkfRliWJ5

How to use "reflect" to set interface value inside a struct of struct

Had a rough time trying to set the interface value by using "reflect" package. The interface value is actually inside a struct of a struct. See my code in Go Playground
Basically, inside initProc, I want to assign dummyAFunc function to DummyA field in Box struct
package main
import (
"fmt"
"reflect"
)
type Box struct {
Name string
DummyA interface{}
}
type SmartBox struct {
Box
}
func dummyAFunc(i int) {
fmt.Println("dummyAFunc() is here!")
}
func initProc(inout interface{}) {
// Using "inout interface{}", I can take any struct that contains Box struct
// And my goal is assign dummyAFunc to dummyA in Box struct
iType:=reflect.TypeOf(inout)
iValue:=reflect.ValueOf(inout)
fmt.Println("Type & value:", iType.Elem(), iValue.Elem()) // Type & value: *main.SmartBox &{{ <nil>}}
e := reflect.ValueOf(inout).Elem()
fmt.Println("Can set?", e.CanSet()). // true
fmt.Println("NumField", e.NumField()) // panic: reflect: call of reflect.Value.NumField on ptr Value ?????
fmt.Println("NumMethod", e.NumMethod()) // NumMethod = 0
}
func main() {
smartbox := new (SmartBox)
initProc(&smartbox)
}
I'm new to Go and I've read the The laws of Reflection but still can't figure it out. Please help. Thanks!
You are passing a **SmartBix to initProc. So when you dereference once with reflect's Elem() you are still getting a pointer (*Smart box).
Since new already returns a pointer, just use:
smartbox := new (SmartBox)
// InitProc(smartbox) // **SmartBox
InitProc(smartbox) // *SmartBox
https://play.golang.org/p/j4q6aq6QL_4
EDIT
To update the input struct's DummyA field, you can do something like this:
func initProc2(v interface{}) error {
if reflect.TypeOf(v).Kind() != reflect.Ptr {
return fmt.Errorf("value must be a pointer")
}
dv := reflect.ValueOf(v).Elem()
if dv.Kind() != reflect.Struct {
return fmt.Errorf("value must be a pointer to a struct/interface")
}
const fname = "DummyA" // lookup field name
f := dv.FieldByName(fname)
if !f.CanSet() {
return fmt.Errorf("value has no field %q or cannot be set", fname)
}
nv := reflect.ValueOf(dummyAFunc)
f.Set(nv)
return nil
}
Working example: https://play.golang.org/p/VE751GtSGEw

Modify struct fields during instance generation

Foreign application API gives me a list of names in JSON format. I need modify all of those.
But I do not like to write some loop for it (especially after Python using with reflection and stuff)
Is there any method to write something like this in Go?
type MyIncredibleType struct {
Name ModifyName // ModifyName is not a type!
}
func ModifyName(input string) string {
return input + ".com"
}
The expected behavior of this is:
a := MyIncredibleType{Name: "Abracadabra"}
print(a.Name) // Abracadabra.com
This seems pretty straight forward to me, assuming I understand your question correctly:
// ModifyName func
func ModifyName(input string) string {
return fmt.Sprintf("%v.com", input)
}
If you wish to achieve this within the type itself, without modifying (mutating) the internal state:
type MyType sturct {
name string // unexported
}
// accessor func to return name
func (t MyType) Name() string {
return t.name
}
// accessor func to return modified name
func (t MyType) ModifiedName() string {
return fmt.Sprintf("%v.com", t.name)
}
If you want to modify the internal state:
type MyType struct {
name string
}
// mutator func (note the pointer for pass by reference)
func (t *MyType) ModifyName(input string) {
t.name = fmt.Sprintf("%v.com", input)
}
// accessor (note no pointer for pass by value)
func (t MyType) Name() string {
return t.name
}
This is is not possible in GO. That's not how struct works in Go.
type MyIncredibleType struct {
Name ModifyName `json:"name"` // ModifyName is not a type!
}
you can only define Built-in types for your fields of struct or you can define Composite Literal types.
Composite literals construct values for structs, arrays, slices, and
maps and create a new value each time they are evaluated. They consist
of the type of the literal followed by a brace-bound list of elements.
Each element may optionally be preceded by a corresponding key.
Try to create a method receiver of struct which you are using to parse json coming from the api to modify the name. That will let you achieve something similar to what you want.
package main
import (
"fmt"
)
type MyIncredibleType struct {
Name string `json:"name"` // ModifyName is not a type!
}
func(myIncredibleType *MyIncredibleType) ModifyName() string {
return myIncredibleType.Name+".com"
}
func main() {
a := MyIncredibleType{Name: "Abracadabra"}
name := a.ModifyName()
fmt.Printf("%s",name)
}
Playground Example
Or you can pass an interface which will wrap any struct value with name field and then use Type assertion to get the underlying value to modify the same and return the result:
package main
import (
"fmt"
)
type MyIncredibleType struct {
Name string `json:"name"` // ModifyName is not a type!
}
func ModifyName(input interface{}) string{
return input.(interface{}).(string)+".com"
}
func main() {
a := MyIncredibleType{Name: "Abracadabra"}
name := ModifyName(a.Name)
fmt.Printf("%s",name)
}
Working code on Go Playground
For more information also go through Golang method Declarations on how to create receivers.

Golang interface to struct

I have a function that has a parameter with the type interface{}, something like:
func LoadTemplate(templateData interface{}) {
In my case, templateData is a struct, but each time it has a different structure. I used the type "interface{}" because it allows me to send all kind of data.
I'm using this templateData to send the data to the template:
err := tmpl.ExecuteTemplate(w, baseTemplateName, templateData)
But now I want to append some new data and I don't know how to do it because the "interface" type doesn't allow me to add/append anything.
I tried to convert the interface to a struct, but I don't know how to append data to a struct with an unknown structure.
If I use the following function I can see the interface's data:
templateData = appendAssetsToTemplateData(templateData)
func appendAssetsToTemplateData(t interface{}) interface{} {
switch reflect.TypeOf(t).Kind() {
case reflect.Struct:
fmt.Println("struct")
s := reflect.ValueOf(t)
fmt.Println(s)
//create a new struct based on current interface data
}
return t
}
Any idea how can I append a child to the initial interface parameter (templateData)? Or how can I transform it to a struct or something else in order to append the new child/data?
Adrian is correct. To take it a step further, you can only do anything with interfaces if you know the type that implements that interface. The empty interface, interface{} isn't really an "anything" value like is commonly misunderstood; it is just an interface that is immediately satisfied by all types.
Therefore, you can only get values from it or create a new "interface" with added values by knowing the type satisfying the empty interface before and after the addition.
The closest you can come to doing what you want, given the static typing, is by embedding the before type in the after type, so that everything can still be accessed at the root of the after type. The following illustrates this.
https://play.golang.org/p/JdF7Uevlqp
package main
import (
"fmt"
)
type Before struct {
m map[string]string
}
type After struct {
Before
s []string
}
func contrivedAfter(b interface{}) interface{} {
return After{b.(Before), []string{"new value"}}
}
func main() {
b := Before{map[string]string{"some": "value"}}
a := contrivedAfter(b).(After)
fmt.Println(a.m)
fmt.Println(a.s)
}
Additionally, since the data you are passing to the template does not require you to specify the type, you could use an anonymous struct to accomplish something very similar.
https://play.golang.org/p/3KUfHULR84
package main
import (
"fmt"
)
type Before struct {
m map[string]string
}
func contrivedAfter(b interface{}) interface{} {
return struct{
Before
s []string
}{b.(Before), []string{"new value"}}
}
func main() {
b := Before{map[string]string{"some": "value"}}
a := contrivedAfter(b)
fmt.Println(a)
}
You can't append data arbitrarily to a struct; they're statically typed. You can only assign values to the fields defined for that specific struct type. Your best bet is probably to use a map instead of structs for this.
Not recommended, but you can create structs dynamically using the reflect package.
Here is an example:
package main
import (
"encoding/json"
"os"
"reflect"
)
type S struct {
Name string
}
type D struct {
Pants bool
}
func main() {
a := Combine(&S{"Bob"}, &D{true})
json.NewEncoder(os.Stderr).Encode(a)
}
func Combine(v ...interface{}) interface{} {
f := make([]reflect.StructField, len(v))
for i, u := range v {
f[i].Type = reflect.TypeOf(u)
f[i].Anonymous = true
}
r := reflect.New(reflect.StructOf(f)).Elem()
for i, u := range v {
r.Field(i).Set(reflect.ValueOf(u))
}
return r.Addr().Interface()
}
You could use something like the Combine function above to shmush any number of structs together. Unfortunately, from the documentation:
StructOf currently does not generate wrapper methods for embedded fields. This limitation may be lifted in a future version.
So your created struct won't inherit methods from the embedded types. Still, maybe it does what you need.
If you are just looking to convert your interface to struct, use this method.
type Customer struct {
Name string `json:"name"`
}
func main() {
// create a customer, add it to DTO object and marshal it
receivedData := somefunc() //returns interface
//Attempt to unmarshall our customer
receivedCustomer := getCustomerFromDTO(receivedData)
fmt.Println(receivedCustomer)
}
func getCustomerFromDTO(data interface{}) Customer {
m := data.(map[string]interface{})
customer := Customer{}
if name, ok := m["name"].(string); ok {
customer.Name = name
}
return customer
}

How do you create a new instance of a struct from its type at run time in Go?

In Go, how do you create the instance of an object from its type at run time? I suppose you would also need to get the actual type of the object first too?
I am trying to do lazy instantiation to save memory.
In order to do that you need reflect.
package main
import (
"fmt"
"reflect"
)
func main() {
// one way is to have a value of the type you want already
a := 1
// reflect.New works kind of like the built-in function new
// We'll get a reflected pointer to a new int value
intPtr := reflect.New(reflect.TypeOf(a))
// Just to prove it
b := intPtr.Elem().Interface().(int)
// Prints 0
fmt.Println(b)
// We can also use reflect.New without having a value of the type
var nilInt *int
intType := reflect.TypeOf(nilInt).Elem()
intPtr2 := reflect.New(intType)
// Same as above
c := intPtr2.Elem().Interface().(int)
// Prints 0 again
fmt.Println(c)
}
You can do the same thing with a struct type instead of an int. Or anything else, really. Just be sure to know the distinction between new and make when it comes to map and slice types.
As reflect.New doesn't automatically make reference types used in struct fields, you could use something like the following to recursively initialize those field types (note the recursive struct definition in this example):
package main
import (
"fmt"
"reflect"
)
type Config struct {
Name string
Meta struct {
Desc string
Properties map[string]string
Users []string
}
}
func initializeStruct(t reflect.Type, v reflect.Value) {
for i := 0; i < v.NumField(); i++ {
f := v.Field(i)
ft := t.Field(i)
switch ft.Type.Kind() {
case reflect.Map:
f.Set(reflect.MakeMap(ft.Type))
case reflect.Slice:
f.Set(reflect.MakeSlice(ft.Type, 0, 0))
case reflect.Chan:
f.Set(reflect.MakeChan(ft.Type, 0))
case reflect.Struct:
initializeStruct(ft.Type, f)
case reflect.Ptr:
fv := reflect.New(ft.Type.Elem())
initializeStruct(ft.Type.Elem(), fv.Elem())
f.Set(fv)
default:
}
}
}
func main() {
t := reflect.TypeOf(Config{})
v := reflect.New(t)
initializeStruct(t, v.Elem())
c := v.Interface().(*Config)
c.Meta.Properties["color"] = "red" // map was already made!
c.Meta.Users = append(c.Meta.Users, "srid") // so was the slice.
fmt.Println(v.Interface())
}
You can use reflect.Zero() which will return the representation of the zero value of the struct type. (similar to if you did var foo StructType) This is different from reflect.New() as the latter will dynamically allocate the struct and give you a pointer, similar to new(StructType)
Here's a basic example like Evan Shaw gave, but with a struct:
package main
import (
"fmt"
"reflect"
)
func main() {
type Product struct {
Name string
Price string
}
var product Product
productType := reflect.TypeOf(product) // this type of this variable is reflect.Type
productPointer := reflect.New(productType) // this type of this variable is reflect.Value.
productValue := productPointer.Elem() // this type of this variable is reflect.Value.
productInterface := productValue.Interface() // this type of this variable is interface{}
product2 := productInterface.(Product) // this type of this variable is product
product2.Name = "Toothbrush"
product2.Price = "2.50"
fmt.Println(product2.Name)
fmt.Println(product2.Price)
}
Per newacct's response, using Reflect.zero it would be:
var product Product
productType := reflect.TypeOf(product) // this type of this variable is reflect.Type
productValue := reflect.Zero(productType) // this type of this variable is reflect.Value
productInterface := productValue.Interface() // this type of this variable is interface{}
product2 := productInterface.(Product) // the type of this variable is Product
This is a great article on the basics of reflection in go.
You don't need reflect and you can do this easy with factory pattern if they share the same interface:
package main
import (
"fmt"
)
// Interface common for all classes
type MainInterface interface {
GetId() string
}
// First type of object
type FirstType struct {
Id string
}
func (ft *FirstType) GetId() string {
return ft.Id
}
// FirstType factory
func InitializeFirstType(id string) MainInterface {
return &FirstType{Id: id}
}
// Second type of object
type SecondType struct {
Id string
}
func (st *SecondType) GetId() string {
return st.Id
}
// SecondType factory
func InitializeSecondType(id string) MainInterface {
return &SecondType{Id: id}
}
func main() {
// Map of strings to factories
classes := map[string]func(string) MainInterface{
"first": InitializeFirstType,
"second": InitializeSecondType,
}
// Create a new FirstType object with value of 10 using the factory
newObject := classes["first"]("10")
// Show that we have the object correctly created
fmt.Printf("%v\n", newObject.GetId())
// Create a new SecondType object with value of 20 using the factory
newObject2 := classes["second"]("20")
// Show that we have the object correctly created
fmt.Printf("%v\n", newObject2.GetId())
}

Resources