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
Related
I want to achieve polymorfism by passing a pointer to a slice of a speficic interface to a function, and update the slice inside of the function. It works quite well with interface{}
package main
import (
"fmt"
"strconv"
)
type valuer interface {
value() string
}
type myInt int
func (i myInt) value() string {
return strconv.Itoa(int(i))
}
func values(vals interface{}) {
res, ok := vals.(*[]myInt)
if !ok {
panic("wrong type")
}
*res = []myInt{1, 2, 3}
}
func main() {
var a []myInt
values(&a)
for _, b := range a {
fmt.Println(b.value())
}
}
Go Playground
However if I try to change interface{} to a pointer to a slice of a specific interface it does not work:
package main
import (
"fmt"
"strconv"
)
type valuer interface {
value() string
}
type myInt int
func (i myInt) value() string {
return strconv.Itoa(int(i))
}
func values(vals *[]valuer) {
*vals = []myInt{1, 2, 3}
}
func main() {
var a []myInt
values(&a)
for _, b := range a {
fmt.Println(b.value())
}
}
Go Playground
returning an error
./prog.go:19:8: cannot use []myInt literal (type []myInt) as type []valuer in assignment
./prog.go:24:9: cannot use &a (type *[]myInt) as type *[]valuer in argument to values
What am I doing wrong?
From the insights provided by #kostix, I can see that I cannot keep both -- a restriction of a non-empty interface, and a simplicity of passing a pointer of a slice of a concrete type. So if I do want to keep the output as a slice of non-empty interfaces, I can do something of this sort instead:
package main
import (
"fmt"
"strconv"
)
type valuer interface {
value() string
}
type myInt int
func (i myInt) value() string {
return strconv.Itoa(int(i))
}
func values() []valuer {
res := make([]valuer, 3)
c := []myInt{1, 2, 3}
for i, v := range c {
res[i] = v
}
return res
}
func main() {
a := values()
for _, b := range a {
fmt.Println(b.value())
}
}
Go Playground
It would keep api simpler to use, and allow polymorphism with non-empty interfaces for the output.
One inconvenience for the users with this approach, they will have to "unbox" members of the slice if they want to use methods of a concrete type that are not specified by the interface.
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
I am trying to get embedded type from Go structs. Below is an example program that demonstrates this. Is there a way to write myfunc() without enumerating every type that can come in as input?
https://play.golang.org/p/5wp14O660m
package main
import (
"fmt"
)
type ObjectMeta struct {
Name string
Namespace string
}
type A struct {
ObjectMeta
X string
}
type B struct {
ObjectMeta
X string
}
func myfunc(v interface{}) ObjectMeta {
switch u := v.(type) {
case *A:
return u.ObjectMeta
case A:
return u.ObjectMeta
case *B:
return u.ObjectMeta
case B:
return u.ObjectMeta
}
panic("No matching type")
}
func main() {
fmt.Println(myfunc(&A{}))
var v interface{} = &A{}
fmt.Println(v.(*ObjectMeta))
}
ObjectMeta, A, B structs exist in external project. I have no control over them.
It can be done using reflection, iterating through the fields of the incoming value:
func myfunc(v interface{}) ObjectMeta {
// Elem() to de-reference pointer
ifv := reflect.ValueOf(v).Elem()
ift := reflect.TypeOf(v).Elem()
for i := 0; i < ift.NumField(); i++ {
f := ift.Field(i)
if f.Name == "ObjectMeta" {
fv := ifv.Field(i)
return fv.Interface().(ObjectMeta)
}
}
panic("ObjectMeta not found")
}
Playground: https://play.golang.org/p/CzMHJWhxYr
You can define interface which will get you that embedded type:
package main
import (
"fmt"
)
type HasMeta interface {
GetMeta() ObjectMeta
}
type ObjectMeta struct {
Name string
Namespace string
}
func (o ObjectMeta) GetMeta() ObjectMeta {
return o
}
type A struct {
ObjectMeta
X string
}
type B struct {
ObjectMeta
X string
}
func myfunc(o HasMeta) ObjectMeta {
return o.GetMeta()
}
func main() {
fmt.Println(myfunc(&A{}))
fmt.Println(myfunc(A{}))
fmt.Println(myfunc(&B{}))
fmt.Println(myfunc(B{}))
}
https://play.golang.org/p/CWa4k-kvvl
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()
}
}
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))
}