Convert interface to struct - go

type SipField interface {
Info() (id, name, defaultValue string, length int)
}
type Field string
func (f *Field) Get() string {
return string(*f)
}
func (f *Field) Set(s string) {
*f = Field(s)
}
type CommandID Field
func (cid *CommandID) Info() (id, name, defaultValue string, length int) {
return "", "command ID", "", 2
}
type Language Field
func (l *Language) Info() (id, name, defaultValue string, length int)
{
return "", "language", "019", 3
}
func InitField(f interface{}, val string) error {
sipField, ok := f.(SipField)
if !ok {
return errors.New("InitField: require a SipField")
}
_, _, defaultValue, length := sipField.Info()
field, ok := f.(*Field)
if !ok {
return errors.New("InitField: require a *Field")
}
return nil
}
How should I do for converting interface{} to Field(CommandID, Language...) in InitField() function? I try to directly type assert by
field, ok := f.(*Field)
but it not working.I have tried to use unsafe.Pointer but failed also.

Have a look at Type assertions chapter in Go reference. It states:
x.(T)
More precisely, if T is not an interface type, x.(T) asserts that the dynamic type of x is identical to the type T.
Types CommandID and Field are not identical as described in Type identity.
A defined type is always different from any other type.
Both types CommandId and Fields are defined as described in Type definitions.
A type definition creates a new, distinct type with the same underlying type and operations as the given type, and binds an identifier to it.
TypeDef = identifier Type .
You can only do
field, ok := f.(*CommandID)
or
field, ok := f.(*Language)
As #mkopriva mentioned in the comment, you can do type conversion later to *Field but this it does not seem to be your goal.
Other solution is to introduce a Field interface with Set and Get methods. Then you will need to provide an implementation for every implementing type.

Related

Transform values when umarshaling

Given the code below, is it possible to transform first name while it's being unmarshaled? Say I want to always have it be lowercase whether it is in the actual json or not.
type Person struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
}
func main() {
jsonText := GetJsonFromSomewhere()
var v Person
json.Unmarshal(jsonText, &v)
}
One way to do this is to create a custom type that implements the Unmarshaler interface from the encoding/json package. Here's a link to this interface. Any type which implements Unmarshaler can be used as the type of a struct field when doing JSON unmarshalling. When doing the unmarshalling, encoding/json will use your implementation of the interface's UnmarshalJSON function to convert the JSON bytes to the field type.
So you could write an UnmarshalJSON function which includes changing the string values to lowercase.
Here's an example of what that could look like:
type LowerCaseString string
func (l *LowerCaseString) UnmarshalJSON(bytes []byte) error {
lowerCasedString := strings.ToLower(string(bytes))
*l = LowerCaseString(lowerCasedString)
return nil
}
Then, in your struct for JSON mapping, you can use your custom type instead of string:
type Person struct {
FirstName LowerCaseString `json:"first_name"`
LastName LowerCaseString `json:"last_name"`
}
If you unmarshal into this struct, the values of FirstName and LastName will be lowercased (also note that you'll need to type convert them back to string to use them as strings).
testJSON := `{"first_name" : "TestFirstNAME", "last_name": "TestLastNAME"}`
var result Person
err := json.Unmarshal([]byte(testJSON), &result)
if err != nil { /*handle the error*/ }
fmt.Println(result.FirstName) // prints "testfirstname"
var stringLastName string
stringLastName = string(result.LastName) // need to type convert from LowerCaseString to string
fmt.Println(stringLastName) // prints "testlastname"
Here is the above code running in Go Playground.

Golang Pointer Understanding

I am learning go and i need to understand something. I am getting few errors. I have created a Product struct and attached a func with it. I also got a product lists as a slice. Actually I am following one example. I was just trying add different endpoints to it.
I have added question in comment in code. Please explain. I need to return the json single object as a response to user. Please guide me.
package data
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Price float32 `json:"price"`
SKU string `json:"sku"`
CreatedOn string `json:"-"`
UpdatedOn string `json:"-"`
DeletedOn string `json:"-"`
}
type Products []*Product
func (p *Products) ToJSON(w io.Writer) error {
e := json.NewEncoder(w)
return e.Encode(p)
}
func (p *Product) FromJSON(r io.Reader) error {
d := json.NewDecoder(r)
return d.Decode(p)
}
var ErrProductNotFound = fmt.Errorf("Product not found")
func GetProduct(id int) (*Product, error) { // this is returning *Product & err. When I use this in GetProduct in handler func it is giving error
for _, p := range productList {
if p.ID == id {
fmt.Println(p)
return p, nil
}
}
return nil, ErrProductNotFound
}
var productList = []*Product{ **// Why in example the teacher doing it like this.** []*Product{&Product{}, &Product{}} **what it the reason? Please explain.
&Product{ // this gives warning : redundant type from array, slice, or map composite literal. need to understand why**
ID: 1,
Name: "Latte",
Description: "chai",
Price: 2.45,
SKU: "abc123",
CreatedOn: time.Now().UTC().String(),
UpdatedOn: time.Now().UTC().String(),
},
&Product{
ID: 2,
Name: "Tea",
Description: "chai",
Price: 1.45,
SKU: "abc1234",
CreatedOn: time.Now().UTC().String(),
UpdatedOn: time.Now().UTC().String(),
},
}
package handlers
func (p *Product) GetProduct(rw http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
p, errr := data.GetProduct(id) **// cannot use data.GetProduct(id) (value of type *data.Product) as *Product value in assignment**
errr = p.ToJSON(rw) // **p.ToJSON undefined (type *Product has no field or method ToJSON)**
if errr != nil {
http.Error(rw, "could not locate the product", http.StatusBadGateway)
}
}
cannot use data.GetProduct(id) (value of type *data.Product) as *Product value in assignment
p.ToJSON undefined (type *Product has no field or method ToJSON)
The problem here is that inside the GetProduct handler the variable p already has a type (*handlers.Product) that is different from the one returned by the data.GetProduct function (*data.Product). So what you can do is to use a different name for the variable that will store the result of data.GetProduct.
Why in example the teacher doing it like this. []*Product{&Product{}, &Product{}} what it the reason? Please explain.
In general because that's one of the available methods for how to initialize a slice of structs, as per the language spec. Why the teacher used this method specifically? Unless the teacher confided the reason to someone, then no one would know, I certainly don't.
this gives warning : redundant type from array, slice, or map composite literal. need to understand why
Because it's true, it is redundant, as per the language spec, in a composite literal expression you can elide the types of the elements and keys.
For example a non-redundant version of the following composite literal:
[]*Product{&Product{}, &Product{}}
would look like this:
[]*Product{{}, {}}
and the result of these two expressions would be the same.

Unable to use type string as sql.NullString

I am creating a gorm model
// Day is a corresponding day entry
type Day struct {
gorm.Model
Dateday string `json:"dateday" gorm:"type:date;NOT NULL"`
Nameday string `json:"nameday" gorm:"type:varchar(100);NOT NULL"`
Something sql.NullString `json:"salad"`
Holyday bool `json:"holyday"`
}
I am using sql.NullString for the field Something cause it may be NULL.
So when I try to execute a typical gorm example to validate my setup works:
db.Create(&Day{
Nameday: "Monday",
Dateday: "23-10-2019",
Something: "a string goes here",
Holyday: false,
})
I get:
cannot use "a string goes here", (type string) as type sql.NullString in field value
What type should I use for the Something field given it may be NULL?
The sql.NullString type is not actually a string type but a struct type. It's defined as:
type NullString struct {
String string
Valid bool // Valid is true if String is not NULL
}
Therefore you need to initialize it as such:
db.Create(&Day{
Nameday: "Monday",
Dateday: "23-10-2019",
Something: sql.NullString{String: "a string goes here", Valid: true},
Holyday: false,
})
As an alternative, if you want to keep using the simpler syntax when initializing a nullable string, you could declare your own nullable string type, have it implement the sql.Scanner and driver.Valuer interfaces, and leverage the null byte to signal a NULL value.
type MyString string
const MyStringNull MyString = "\x00"
// implements driver.Valuer, will be invoked automatically when written to the db
func (s MyString) Value() (driver.Value, error) {
if s == MyStringNull {
return nil, nil
}
return []byte(s), nil
}
// implements sql.Scanner, will be invoked automatically when read from the db
func (s *String) Scan(src interface{}) error {
switch v := src.(type) {
case string:
*s = String(v)
case []byte:
*s = String(v)
case nil:
*s = StringNull
}
return nil
}
With this, if you declare the field Something to be of type MyString you can initialize it as you originally intended.
db.Create(&Day{
Nameday: "Monday",
Dateday: "23-10-2019",
// here the string expression is an *untyped* string constant
// that will be implicitly converted to MyString because
// both `string` and `MyString` have the same *underlying* type.
Something: "a string goes here",
Holyday: false,
})
Just keep in mind that this works only with untyped constants, once you have a constant or variable of type string, to be able to assign that to a MyString you'll need to use an explicit conversion.
var s string
var ms MyString
s = "a string goes here"
ms = s // won't compile because s is not an untyped constant
ms = MyString(s) // you have to explicitly convert
package main
import (
"github.com/guregu/null"
)
func main() {
db.Create(&Day{
Nameday: null.StringFrom("Monday"),
})
}

Creating array of struct dynamically in golang

I try to create a generic function that accepts any struct value and create a array of that struct type. Here is the code I tried. But I get the error "t is not a type". How can I implement this.
type RegAppDB struct {
nm string
data []interface{}
}
func CreateRegTable(tbl string, rec interface{}) RegAppDB {
t := reflect.TypeOf(rec)
fmt.Println(t)
return RegAppDB{"log", []t}
}
Go does not support generics, and any attempt to do something like that is not going to work out well. In your specific case, there are a couple of key problems:
You cannot use a variable as a type. Go is compile-time static typed, so anything that gets type information at runtime (i.e. reflect.TypeOf) happens too late to use the way you're trying to do it.
Equally important, your struct's field is of type []interface{}, which means the only type you can use for that field is []interface{}. []string, for example, is a different type, and cannot be assigned to that field.
I took another route. Need some beautification. But this works. So now data is an array of interface and from calling function I pass pointer to structure variables to Save function.
type RegAppDB struct {
nm string
data []interface{}
cnt int
}
// CreateRegTable creates a data structure to hold the regression result
func CreateRegTable(tbl string) *RegAppDB {
return &RegAppDB{tbl, make([]interface{}, 20), 0}
}
// Save implements saving a record Regression application DB
func (rga *RegAppDB) Save(rec interface{}) error {
rga.data[rga.cnt] = rec
rga.cnt++
return nil
}
// Show implements showing the regression table
func (rga *RegAppDB) Show() error {
fmt.Println(rga.cnt)
for i := 0; i <= rga.cnt; i++ {
fmt.Println(rga.data[i])
}
return nil
}
// Compare compares two regression table for equality
func (rga *RegAppDB) Compare(rgt *RegAppDB) bool {
return reflect.DeepEqual(rga, rgt)
}
It cannot be done for a generic type. If there are a fixed number of possible types, then you can do something like the following:
type RegAppDB struct {
nm string
data interface{}
}
func CreateRegTable(rec interface{}) RegAppDB {
switch rec.(type) {
case int:
return &RegAppDB{"log", []int{}}
case string:
return &RegAppDB{"log", []string{}}
}
return nil
}
Note: Your data in RegAppDB should be of type interface{} since []int implements interface{} but not []interface{}

How to check if a value implements an interface

I want to compare my type by the specific way. For this purpose, I create the function MyType.Same(other MyType) bool for each type.
In some generic function, I want to check if the parameter has a function "Same" and invoke it if yes.
How can I do it in a generic way for different types?
type MyType struct {
MyField string
Id string // ignored by comparison
}
func (mt MyType) Same(other MyType) bool {
return mt.MyField == other.MyField
}
// MyOtherType... Same(other MyOtherType)
type Comparator interface {
Same(Camparator) bool // or Same(interface{}) bool
}
myType = new(MyType)
_, ok := reflect.ValueOf(myType).Interface().(Comparator) // ok - false
myOtherType = new(myOtherType)
_, ok := reflect.ValueOf(myOtherType).Interface().(Comparator) // ok - false
The types do not satisfy the Comparator interface. The types have a Same method, but those methods do not have argument type Comparator. The argument types must match to satisfy an interface.
Change the the methods and interface to take the same argument type. Use a type assertion to check that receiver and argument have the same type and to get argument as the receiver's type.
type Comparator interface {
Same(interface{}) bool
}
func (mt MyType) Same(other interface{}) bool {
mtOther, ok := other.(MyType)
if !ok {
return false
}
return return mt.MyField == mtOther.MyField
}
Use the following to compare two values:
func same(a, b interface{}) bool {
c, ok := a.(Comparator)
if !ok {
return false
}
return c.Same(b)
}
If the types the application works with have the Compare method, then there's no need to declare the Comparator interface or use the same function in the previous snippet of code. For example, the Comparator interface is not required for the following:
var mt MyType
var other interface{}
eq := mt.Same(other)

Resources