How can I call dbmap.Insert(interface{}) from another function? - go

I have a bunch of very similar structs (A and B in the example) whose instances I want to handle in some function ( f() in the example) and then insert them into my database. I figured I could handle that with the empty interface somehow, but it seems this is not the solution as I get the error:
i: &{{6 2019-04-03 15:11:37.822100431 +0200 CEST m=+0.001291882} 7} *main.A
2019/04/03 15:11:37 Insert i no table found for type:
exit status 1
I tried to create some minimal but executable example:
package main
import (
"database/sql"
"fmt"
_ "github.com/mattn/go-sqlite3"
gorp "gopkg.in/gorp.v2"
"log"
"time"
)
type Meta struct {
Id int
CreatedAt time.Time
}
type A struct {
Meta
Value int
}
type B struct {
Meta
Value string
}
var dbmap *gorp.DbMap
func f(i interface{}) {
fmt.Printf("i: %v %T\n", i, i)
err := dbmap.Insert(&i)
checkErr(err, "Insert i")
}
func main() {
Init()
a := A{Meta: Meta{CreatedAt: time.Now()}, Value: 7}
b := B{Meta: Meta{CreatedAt: time.Now()}, Value: "seven"}
err := dbmap.Insert(&a) // works
checkErr(err, "Insert a")
err = dbmap.Insert(&b) // works
checkErr(err, "Insert b")
f(&a) // fails
}
func Init() {
db, err := sql.Open("sqlite3", "/tmp/post_db.bin")
checkErr(err, "sql.Open failed")
dbmap = &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}
dbmap.AddTableWithName(A{}, "As").SetKeys(true, "Id")
dbmap.AddTableWithName(B{}, "Bs").SetKeys(true, "Id")
err = dbmap.CreateTablesIfNotExists()
checkErr(err, "Couldn't create tables")
}
func checkErr(err error, msg string) {
if err != nil {
log.Fatalln(msg, err)
}
}
What's the right way to do that? In C++ I'd simply use templates ;)

If you are calling you func like f(&a). You should call inside func f just dbmap.Insert(i), because your value is already a pointer. So your func will look like
func f(i interface{}) {
fmt.Printf("i: %v %T\n", i, i)
err := dbmap.Insert(i)
checkErr(err, "Insert i")
}

Related

How to omit empty json fields using json.decoder

I try to understand why both functions return the same output.
As far as I understood, the point of omit empty is to not add that key to the result struct.
I wrote this example, I was expecting the first output not to have the "Empty" key, but for some reason its value still shows as 0.
package main
import (
"encoding/json"
"fmt"
"strings"
)
type agentOmitEmpty struct {
Alias string `json:"Alias,omitempty"`
Skilled bool `json:"Skilled,omitempty"`
FinID int32 `json:"FinId,omitempty"`
Empty int `json:"Empty,omitempty"`
}
type agent struct {
Alias string `json:"Alias"`
Skilled bool `json:"Skilled"`
FinID int32 `json:"FinId"`
Empty int `json:"Empty"`
}
func main() {
jsonString := `{
"Alias":"Robert",
"Skilled":true,
"FinId":12345
}`
fmt.Printf("output with omit emtpy: %v\n", withEmpty(strings.NewReader(jsonString)))
// output with omit emtpy: {Robert true 12345 0}
fmt.Printf("output regular: %v\n", withoutEmpty(strings.NewReader(jsonString)))
// output without omit: {Robert true 12345 0}
}
func withEmpty(r *strings.Reader) agentOmitEmpty {
dec := json.NewDecoder(r)
body := agentOmitEmpty{}
err := dec.Decode(&body)
if err != nil {
panic(err)
}
return body
}
func withoutEmpty(r *strings.Reader) agent {
dec := json.NewDecoder(r)
body := agent{}
err := dec.Decode(&body)
if err != nil {
panic(err)
}
return body
}
You need to define Empty as *int so it will be replaced with nil when there is no value. Then it will not be saved in the database.

Problem with Marshal/unMarshal when key of map is a struct

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

Timestamps in Golang

Trying to get this approach to timestamps working in my application: https://gist.github.com/bsphere/8369aca6dde3e7b4392c#file-timestamp-go
Here it is:
package timestamp
import (
"fmt"
"labix.org/v2/mgo/bson"
"strconv"
"time"
)
type Timestamp time.Time
func (t *Timestamp) MarshalJSON() ([]byte, error) {
ts := time.Time(*t).Unix()
stamp := fmt.Sprint(ts)
return []byte(stamp), nil
}
func (t *Timestamp) UnmarshalJSON(b []byte) error {
ts, err := strconv.Atoi(string(b))
if err != nil {
return err
}
*t = Timestamp(time.Unix(int64(ts), 0))
return nil
}
func (t Timestamp) GetBSON() (interface{}, error) {
if time.Time(*t).IsZero() {
return nil, nil
}
return time.Time(*t), nil
}
func (t *Timestamp) SetBSON(raw bson.Raw) error {
var tm time.Time
if err := raw.Unmarshal(&tm); err != nil {
return err
}
*t = Timestamp(tm)
return nil
}
func (t *Timestamp) String() string {
return time.Time(*t).String()
}
and the article that goes with it: https://medium.com/coding-and-deploying-in-the-cloud/time-stamps-in-golang-abcaf581b72f
However, I'm getting the following error:
core/timestamp/timestamp.go:31: invalid indirect of t (type Timestamp)
core/timestamp/timestamp.go:35: invalid indirect of t (type Timestamp)
My relevant code looks like this:
import (
"github.com/path/to/timestamp"
)
type User struct {
Name string
Created_at *timestamp.Timestamp `bson:"created_at,omitempty" json:"created_at,omitempty"`
}
Can anyone see what I'm doing wrong?
Related question
I can't see how to implement this package either. Do I create a new User model something like this?
u := User{Name: "Joe Bloggs", Created_at: timestamp.Timestamp(time.Now())}
Your code has a typo. You can't dereference a non-pointer, so you need to make GetBSON a pointer receiver (or you could remove the indirects to t, since the value of t isn't changed by the method).
func (t *Timestamp) GetBSON() (interface{}, error) {
To set a *Timestamp value inline, you need to have a *time.Time to convert.
now := time.Now()
u := User{
Name: "Bob",
CreatedAt: (*Timestamp)(&now),
}
Constructor and a helper functions like New() and Now() may come in handy for this as well.
You cannot refer to an indirection of something that is not a pointer variable.
var a int = 3 // a = 3
var A *int = &a // A = 0x10436184
fmt.Println(*A == a) // true, both equals 3
fmt.Println(*&a == a) // true, both equals 3
fmt.Println(*a) // invalid indirect of a (type int)
Thus, you can not reference the address of a with *a.
Looking at where the error happens:
func (t Timestamp) GetBSON() (interface{}, error) {
// t is a variable type Timestamp, not type *Timestamp (pointer)
// so this is not possible at all, unless t is a pointer variable
// and you're trying to dereference it to get the Timestamp value
if time.Time(*t).IsZero() {
return nil, nil
}
// so is this
return time.Time(*t), nil
}

golang type conversion in unmarshaljson

Could someone help me please what's going wrong here? For some reason the output are not the same and I don't get why.
type rTime time.Time
func (rt *rTime) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
t, err := time.Parse("2006-01-02", s)
if err != nil {
return err
}
log.Println(t)
*rt = rTime(t)
log.Println(*rt)
return nil
}
Log looks like this:
2014/09/18 04:31:35 1999-10-15 00:00:00 +0000 UTC
2014/09/18 04:31:35 {63075542400 0 0x933ea0}
Why's the conversion not working? The input string is 1995-10-15 btw.
The conversion is working, but fmt.Println() looks for a String() method, and that exists on time.Time but not on your type. You should need nothing more than func (rt rTime) String() string { return time.Time(rt).String() } to direct String() calls back to time.Time's implementation.
Here's an example:
package main
import (
"log"
"time"
)
type rTime time.Time
func (rt rTime) String() string { return time.Time(rt).String() }
func main() {
s := "1999-10-15"
t, err := time.Parse("2006-01-02", s)
if err != nil {
panic(err)
}
log.Println(t)
rt := rTime(t)
log.Println(rt)
}
Note that I treated both time types as values because the standard library does, per the canonical advice to avoid pointers for tiny structs with value semantics.
Maybe more interesting, you can use type embedding to automagically pick up all of the methods of time.Time except any you override. The syntax changes slightly (see on Playground):
package main
import (
"log"
"time"
)
type rTime struct { time.Time }
func main() {
s := "1999-10-15"
t, err := time.Parse("2006-01-02", s)
if err != nil {
panic(err)
}
log.Println(t)
rt := rTime{t}
log.Println(rt)
}
If you've used embedding and want to write your own custom methods that "proxy through" to the embedded type's, you use a syntax like obj.EmbeddedTypeName.Method, which could be like, for instance, rt.Time.String():
// a custom String method that adds smiley faces
func (rt rTime) String() string { return "😁 " + rt.Time.String() + " 😁" }
obj.EmbeddedTypeName is also how you (for example) access operators on non-struct types that you've embedded.

Golang - unmarshal extra XML attributes

Is there a way to unmarshal XML tags with dynamic attributes (I don't know which attributes I'll get every time).
Maybe it's not supported yet. See Issue 3633: encoding/xml: support for collecting all attributes
Something like :
package main
import (
"encoding/xml"
"fmt"
)
func main() {
var v struct {
Attributes []xml.Attr `xml:",any"`
}
data := `<TAG ATTR1="VALUE1" ATTR2="VALUE2" />`
err := xml.Unmarshal([]byte(data), &v)
if err != nil {
panic(err)
}
fmt.Println(v)
}
As of late 2017, this is supported by using:
var v struct {
Attributes []xml.Attr `xml:",any,attr"`
}
Please see https://github.com/golang/go/issues/3633
You need to implement your own XMLUnmarshaler
package main
import (
"encoding/xml"
"fmt"
)
type CustomTag struct {
Name string
Attributes []xml.Attr
}
func (c *CustomTag) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
c.Name = start.Name.Local
c.Attributes = start.Attr
return d.Skip()
}
func main() {
v := &CustomTag{}
data := []byte(`<tag ATTR1="VALUE1" ATTR2="VALUE2" />`)
err := xml.Unmarshal(data, &v)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", v)
}
outputs
&{Name:tag Attributes:[{Name:{Space: Local:ATTR1} Value:VALUE1} {Name:{Space: Local:ATTR2} Value:VALUE2}]}
http://play.golang.org/p/9ZrpIT32o_

Resources