How to store different structs in a interface for json - go

http://play.golang.org/p/JJnU5ag234
I can only make vA work directly, if I want to use my vI to store A or B in it depending on the json I expect, I get
json: cannot unmarshal object into Go value of type main.TA
package main
import (
"encoding/json"
"fmt"
"strings"
)
type T interface {
Printer()
}
type A struct{ JA string }
func (t A) Printer() { fmt.Print("A") }
type B struct{ JB string }
func (t B) Printer() { fmt.Print("B") }
var vA []A
var vB []B
var vI []T
func main() {
// vA = []A{A{}}
// vI = []T{B{}}
vI = []T{A{}}
get()
}
func get() {
dec := json.NewDecoder(strings.NewReader("[{\"JA\":\"OK\"}]"))
if err := dec.Decode(&vI); err != nil {
fmt.Print(err)
}
for _, v := range vI {
v.Printer()
}
}

Since you expect the decoder to fill the fields of a struct, you have to use pointers. Define methods of the interface on the pointer of the type like this: http://play.golang.org/p/WUMt9Ok9Xp
package main
import (
"encoding/json"
"fmt"
"strings"
)
type T interface {
Printer()
}
type A struct {
JA string
}
func (a *A) Printer() {
fmt.Printf("A: %v\n", a.JA)
}
type B struct {
JB string
}
func (b *B) Printer() {
fmt.Printf("B: %v\n", b.JB)
}
func main() {
vI := []T{&A{}, &B{}}
dec := json.NewDecoder(strings.NewReader("[{\"JA\":\"OKA\"}, {\"JB\":\"OKB\"}]"))
if err := dec.Decode(&vI); err != nil {
fmt.Print(err)
}
for _, v := range vI {
v.Printer()
}
}

Related

how to using reflect to create parametric entity

package main
import (
"fmt"
"reflect"
)
type Aservice struct {
}
type Adata struct {
msg string
}
type Bdata struct {
more string
}
var amap map[string]interface{} = make(map[string]interface{}, 1024)
func (aser *Aservice) Bar(data *Adata) error {
return nil
}
func (aser *Aservice) Foo(data *Bdata) error {
return nil
}
func main() {
var ser *Aservice
typeOfService := reflect.TypeOf(ser)
valueOfService := reflect.ValueOf(ser)
for i := 0; i < valueOfService.NumMethod(); i++ {
nref := valueOfService.Method(i).Type().In(0)
fmt.Println("++", nref.Elem().Name())
amap[typeOfService.Method(i).Name] = nref
}
}
Currently "Adata" and "Bdata" can be printed correctly
But I don’t know how to store the empty structure pointers of "Adata" and "Bdata" in amap
No idea for the next step
I want to use Method(i).Name() in amap to store the parameters that need to be passed in for the Method
Based on the suggestions in comments :
package main
import (
"fmt"
"reflect"
)
type Aservice struct {
}
type Adata struct {
msg string
}
type Bdata struct {
more string
}
var amap = map[string]interface{}{}
func (aser *Aservice) Bar(data *Adata) error {
return nil
}
func (aser *Aservice) Foo(data *Bdata) error {
return nil
}
func main() {
var ser *Aservice
typeOfService := reflect.TypeOf(ser)
valueOfService := reflect.ValueOf(ser)
for i := 0; i < valueOfService.NumMethod(); i++ {
nref := valueOfService.Method(i).Type().In(0)
amap[typeOfService.Method(i).Name] = reflect.New(nref.Elem()).Interface()
}
for k, v := range amap {
fmt.Printf("%s %#v\n", k, v)
}
}
Output:
Bar &main.Adata{msg:""}
Foo &main.Bdata{more:""}

How to transform any string in struct field name like

I would like to know if there is a native way to transform strings like:
a.string
a-string
a_string
a string
In a string that follows the convention for public field members of structs, in Go.
The idea is to write a function that accepts a string and try to get the value of the field, even if the passed string is not using the PascalCase convention, example:
type MyStruct struct {
Debug bool
AString bool
SomethingMoreComplex
}
var myStruct MyStruct
func GetField(s string) reflect.Value {
v := reflect.ValueOf(myStruct)
return v.FieldByName(s)
}
function main() {
GetField("debug")
GetField("a.string")
GetField("a-string")
GetField("a_string")
GetField("-a.string")
GetField("something-more-complex")
}
I was using the strcase package, but it only works for ASCII.
By the magic of regular expressions
https://goplay.space/#xHfxG249CsH
package main
import (
"fmt"
"regexp"
"strings"
)
func ConvertFieldName(s string) string {
r := regexp.MustCompile("(\\b|-|_|\\.)[a-z]")
return r.ReplaceAllStringFunc(s, func(t string) string {
if len(t) == 1 {
return strings.ToUpper(t)
} else {
return strings.ToUpper(string(t[1]))
}
})
}
func main() {
fmt.Println(ConvertFieldName("debug"))
fmt.Println(ConvertFieldName("a.string"))
fmt.Println(ConvertFieldName("a-string"))
fmt.Println(ConvertFieldName("a_string"))
fmt.Println(ConvertFieldName("-a.string"))
fmt.Println(ConvertFieldName("something-more-complex"))
}
Outputs
Debug
AString
AString
AString
AString
SomethingMoreComplex
package main
import (
"fmt"
"reflect"
)
type MyStruct struct {
Debug bool
AString bool
}
var myStruct MyStruct
func GetField(s string) (reflect.Value, error) {
t := reflect.TypeOf(myStruct)
v := reflect.ValueOf(myStruct)
for fieldIndex := 0; fieldIndex < v.NumField(); fieldIndex++ {
if t.Field(fieldIndex).Name == s {
return v.Field(fieldIndex), nil
}
}
return reflect.Value{}, fmt.Errorf("%s not exist", s)
}
func main() {
var v reflect.Value
var err error
v, err = GetField("Debug")
fmt.Println(v, err)
v, err = GetField("debug")
fmt.Println(v, err)
}
the other way, you can try define your own field's tag, like json tag

fmt format specifier to print only field with non-zero value

Consider this code:
package main
import (
"fmt"
)
type myStruct struct {
a string
b int
c bool
}
func main() {
s := myStruct{
c: true,
}
fmt.Printf("%v", s)
// print { 0 true}
fmt.Printf("%+v", s)
// print {a: b:0 c:true}
}
Is there a fmt format specifier to print only fields with non-zero value?
For example, with the code above, how can I print only
{c:true}
because a == "" and b == 0?
There is no built-in format verb that causes zero values to be omitted.
Here are some options.
fmt.Stringer
You can hard-code the string format for your type by implementing fmt.Stringer:
package main
import (
"fmt"
"strings"
)
type myStruct struct {
a string
b int
c bool
}
func (s myStruct) String() string {
var fields []string
if s.a != "" {
fields = append(fields, fmt.Sprintf("a:%q", s.a))
}
if s.b != 0 {
fields = append(fields, fmt.Sprintf("b:%d", s.b))
}
if s.c {
fields = append(fields, fmt.Sprintf("c:%t", s.c))
}
return fmt.Sprintf("{%s}", strings.Join(fields, ","))
}
func main() {
s := myStruct{a: "foo"}
fmt.Println(s)
}
Output:
{a:"foo"}
https://play.golang.org/p/Dw7F4Ua0Eyq
Reflection
You can use reflection to build something that will work with any struct, but it is perhaps more hassle than it is worth. Example omitted.
JSON
Another alternative is to marshal it to JSON, which handles the reflection part and has support for omitting zero values. Example:
package main
import (
"encoding/json"
"log"
"os"
)
type myStruct struct {
A string `json:",omitempty"`
B int `json:",omitempty"`
C bool `json:",omitempty"`
}
func main() {
s := myStruct{A: "foo"}
if err := json.NewEncoder(os.Stdout).Encode(s); err != nil {
log.Fatal(err)
}
}
Output:
{"A":"foo"}
https://play.golang.org/p/NcckEBNdnW6
JSON with unexported fields
If you prefer to keep the original struct as-is; you can define a custom marshaller with an anonymous struct. Note however that the struct format is then duplicated in the MarshalJSON method which adds a bit of complexity:
package main
import (
"encoding/json"
"log"
"os"
)
type myStruct struct {
a string
b int
c bool
}
func (s myStruct) MarshalJSON() ([]byte, error) {
return json.Marshal(
struct {
A string `json:"a,omitempty"`
B int `json:"b,omitempty"`
C bool `json:"c,omitempty"`
}{
A: s.a,
B: s.b,
C: s.c,
},
)
}
func main() {
s := myStruct{a: "foo"}
if err := json.NewEncoder(os.Stdout).Encode(s); err != nil {
log.Fatal(err)
}
}
Output:
{"a":"foo"}
https://play.golang.org/p/qsCKUNeFLpw
JSON with unexported fields + fmt.Stringer
If you want, you can again implement fmt.Stringer, which fmt.Printf and friends will pick up:
package main
import (
"encoding/json"
"fmt"
)
type myStruct struct {
a string
b int
c bool
}
func (s myStruct) MarshalJSON() ([]byte, error) {
return json.Marshal(
struct {
A string `json:"a,omitempty"`
B int `json:"b,omitempty"`
C bool `json:"c,omitempty"`
}{
A: s.a,
B: s.b,
C: s.c,
},
)
}
func (s myStruct) String() string {
j, err := json.Marshal(s)
if err != nil {
return ""
}
return string(j)
}
func main() {
s := myStruct{a: "foo"}
fmt.Println(s)
}
Output:
{"a":"foo"}
https://play.golang.org/p/TPDoLOTAVJo

In go (golang), how can you cast an interface pointer into a struct pointer?

I want to use some external code that requires a pointer to a struct. At the point that the code is called, I have an interface variable.
When creating a pointer off of that variable, the pointer's type is interface{}* when I need it to be the pointer type of the struct's type.
Image the code in TestCanGetStructPointer does not know about the Cat class, and that it exists in some external package.
How can I cast it to this?
Here is a code sample:
import (
"reflect"
"testing"
)
type Cat struct {
name string
}
type SomethingGeneric struct {
getSomething func() interface{}
}
func getSomeCat() interface{} {
return Cat{}
}
var somethingForCats = SomethingGeneric{getSomething: getSomeCat}
func TestCanGetStructPointer(t *testing.T) {
interfaceVariable := somethingForCats.getSomething()
pointer := &interfaceVariable
interfaceVarType := reflect.TypeOf(interfaceVariable)
structPointerType := reflect.PtrTo(interfaceVarType)
pointerType := reflect.TypeOf(pointer)
if pointerType != structPointerType {
t.Errorf("Pointer type was %v but expected %v", pointerType, structPointerType)
}
}
The test fails with:
Pointer type was *interface {} but expected *parameterized.Cat
#dyoo's example does work, but it relies on you to manually cast Dog and Cat.
Here's a bit of a convoluted/verbose example which avoids that constraint somewhat:
package main
import (
"fmt"
"reflect"
)
type Cat struct {
name string
}
type SomethingGeneric struct {
getSomething func() interface{}
}
func getSomeCat() interface{} {
return Cat{name: "Fuzzy Wuzzy"}
}
var somethingForCats = SomethingGeneric{getSomething: getSomeCat}
func main() {
interfaceVariable := somethingForCats.getSomething()
castVar := reflect.ValueOf(interfaceVariable)
castVar.Convert(castVar.Type())
// If you want a pointer, do this:
fmt.Println(reflect.PtrTo(castVar.Type()))
// The deref'd val
if castVar.Type() != reflect.TypeOf(Cat{}) {
fmt.Printf("Type was %v but expected %v\n", castVar, reflect.TypeOf(&Cat{}))
} else {
fmt.Println(castVar.Field(0))
}
}
Playground Link
I found this thread: https://groups.google.com/forum/#!topic/golang-nuts/KB3_Yj3Ny4c
package main
import (
"fmt"
"reflect"
)
type Cat struct {
name string
}
//
// Return a pointer to the supplied struct via interface{}
//
func to_struct_ptr(obj interface{}) interface{} {
fmt.Println("obj is a", reflect.TypeOf(obj).Name())
// Create a new instance of the underlying type
vp := reflect.New(reflect.TypeOf(obj))
// Should be a *Cat and Cat respectively
fmt.Println("vp is", vp.Type(), " to a ", vp.Elem().Type())
vp.Elem().Set(reflect.ValueOf(obj))
// NOTE: `vp.Elem().Set(reflect.ValueOf(&obj).Elem())` does not work
// Return a `Cat` pointer to obj -- i.e. &obj.(*Cat)
return vp.Interface()
}
//
// Dump out a pointer ...
//
func test_ptr(ptr interface{}) {
v := reflect.ValueOf(ptr)
fmt.Println("ptr is a", v.Type(), "to a", reflect.Indirect(v).Type())
}
func main() {
cat := Cat{name: "Fuzzy Wuzzy"}
// Reports "*main.Cat"
test_ptr(&cat)
// Get a "*Cat" generically via interface{}
sp := to_struct_ptr(cat)
// *should* report "*main.Cat" also
test_ptr(sp)
fmt.Println("sp is",sp)
}
The following may help: http://play.golang.org/p/XkdzeizPpP
package main
import (
"fmt"
)
type Cat struct {
name string
}
type Dog struct {
name string
}
type SomethingGeneric struct {
getSomething func() interface{}
}
func getSomeCat() interface{} {
return Cat{name: "garfield"}
}
func getSomeDog() interface{} {
return Dog{name: "fido"}
}
var somethings = []SomethingGeneric{
SomethingGeneric{getSomething: getSomeCat},
SomethingGeneric{getSomething: getSomeDog},
}
func main() {
for _, something := range somethings {
interfaceVariable := something.getSomething()
cat, isCat := interfaceVariable.(Cat)
dog, isDog := interfaceVariable.(Dog)
fmt.Printf("cat %v %v\n", cat, isCat)
fmt.Printf("dog %v %v\n", dog, isDog)
}
}

How to marshal struct when some members are protected/inner/hidden

How do ensure the fields in this LookupCode struct are included when marshalling?
package main
import (
"encoding/json"
"fmt"
)
type LookupCode struct {
code string `json:"code"`
name string `json:"name"`
}
func (l *LookupCode) GetCode() string {
return l.code
}
func main() {
c := &LookupCode{
code: "A",
name: "Apple",
}
b, _ := json.MarshalIndent(c, "", "\t")
fmt.Println(string(b))
}
http://play.golang.org/p/my52DAn0-Z
You can by implementing the json.Marshaller interface:
Full Example: http://play.golang.org/p/8mIcPwX92P
// Implement json.Unmarshaller
func (l *LookupCode) UnmarshalJSON(b []byte) error {
var tmp struct {
Code string `json:"code"`
Name string `json:"name"`
}
err := json.Unmarshal(b, &tmp)
if err != nil {
return err
}
l.code = tmp.Code
l.name = tmp.Name
return nil
}
func (l *LookupCode) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Code string `json:"code"`
Name string `json:"name"`
}{
Code: l.code,
Name: l.name,
})
}
encode/json cannot marshal unexported fields. Change your code to:
type LookupCode struct {
Code string `json:"code"`
Name string `json:"name"`
}
and do the same wherever you use code or name.
Playground: http://play.golang.org/p/rak0nVCNGI
Edit
The limitation is due to the reflection used when marshalling the struct. If you need to keep your values unexported, you must implement the json.Marshaller interface and do the encoding manually.
if the struct has only string-type fields,you can try this hack way.
package main
import (
"fmt"
"reflect"
"github.com/bitly/go-simplejson"
)
type A struct {
name string `json:"name"`
code string `json:"code"`
}
func marshal(a A) ([]byte, error) {
j := simplejson.New()
va := reflect.ValueOf(&a)
vt := va.Elem()
types := reflect.TypeOf(a)
for i := 0; i < vt.NumField(); i++ {
j.Set(types.Field(i).Tag.Get("json"), fmt.Sprintf("%v", reflect.Indirect(va).Field(i)))
}
return j.MarshalJSON()
}
func main() {
a := A{name: "jessonchan", code: "abc"}
b, _ := marshal(a)
fmt.Println(string(b))
}

Resources