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
Related
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:""}
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
I'm not quite sure how to address this question, please feel free to edit.
With the first code block below, I am able to check if a all fields of a struct are nil.
In reality however, the values injected in the struct, are received as args.Review (see second code block below).
In the second code block, how can I check if all fields from args.Review are nil?
Try it on Golang Playground
package main
import (
"fmt"
"reflect"
)
type review struct {
Stars *int32 `json:"stars" bson:"stars,omitempty" `
Commentary *string `json:"commentary" bson:"commentary,omitempty"`
}
func main() {
newReview := &review{
Stars: nil,
// Stars: func(i int32) *int32 { return &i }(5),
Commentary: nil,
// Commentary: func(i string) *string { return &i }("Great"),
}
if reflect.DeepEqual(review{}, *newReview) {
fmt.Println("Nothing")
} else {
fmt.Println("Hello")
}
}
Try the second code on Golang Playground
This code below gets two errors:
prog.go:32:14: type args is not an expression
prog.go:44:27: args.Review is not a type
package main
import (
"fmt"
"reflect"
)
type review struct {
Stars *int32 `json:"stars" bson:"stars,omitempty" `
Commentary *string `json:"commentary" bson:"commentary,omitempty"`
}
type reviewInput struct {
Stars *int32
Commentary *string
}
type args struct {
PostSlug string
Review *reviewInput
}
func main() {
f := &args {
PostSlug: "second-post",
Review: &reviewInput{
Stars: func(i int32) *int32 { return &i }(5),
Commentary: func(i string) *string { return &i }("Great"),
},
}
createReview(args)
}
func createReview(args *struct {
PostSlug string
Review *reviewInput
}) {
g := &review{
Stars: args.Review.Stars,
Commentary: args.Review.Commentary,
}
if reflect.DeepEqual(args.Review{}, nil) {
fmt.Println("Nothing")
} else {
fmt.Println("Something")
}
}
If you're dealing with a small number of fields you should use simple if statements to determine whether they are nil or not.
if args.Stars == nil && args.Commentary == nil {
// ...
}
If you're dealing with more fields than you would like to manually spell out in if statements you could use a simple helper function that takes a variadic number of interface{} arguments. Just keep in mind that there is this: Check for nil and nil interface in Go
func AllNil(vv ...interface{}) bool {
for _, v := range vv {
if v == nil {
continue
}
if rv := reflect.ValueOf(v); !rv.IsNil() {
return false
}
}
return true
}
if AllNil(args.Stars, args.Commentary, args.Foo, args.Bar, args.Baz) {
// ...
}
Or you can use the reflect package to do your bidding.
func NilFields(x interface{}) bool {
rv := reflect.ValueOf(args)
rv = rv.Elem()
for i := 0; i < rv.NumField(); i++ {
if f := rv.Field(i); f.IsValid() && !f.IsNil() {
return false
}
}
return true
}
if NilFields(args) {
// ...
}
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
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))
}