Print looped int as a string - go

I have a custom type looped to display all of the fields given. The purpose of this is to be able to format it a little better than a long, endless single line. However, printing it within the loop won't work, everything I've tried simply prints a string of a single line versus all 4 from the array.
type Rule struct {
Value string `json:"value"`
Tag string `json:"tag"`
Id string `json:"id"`
}
func test() string {
get, err := getRules()
if err != nil {
panic(err)
}
for _, r := range get {
fmt.Printf("value:%v, tag:%v, id:%v\n", r.Value, r.Tag, r.Id)
}
// need to return it as a string like below
return fmt.Sprintf("%v", list)
}
is there a way to easily create a variable list out of that? So I can display it as a string? That prints:
value:x0, tag:t0, id:i0
value:x1, tag:t1, id:i1
value:x2, tag:t2, id:i2
value:x3, tag:t3, id:i3
from fmt.Printf(get)
[{x0 t0 i0} {x1 t1 i1} {x2 t2 i2} {x3 t3 i3}]
I'm essentially trying to convert a custom type to a string so that I can display it better.

You should implement String method.
package main
import (
"fmt"
"strings"
)
type Rule struct {
Value string `json:"value"`
Tag string `json:"tag"`
Id string `json:"id"`
}
func (g *Rule) String() string {
return fmt.Sprintf("value:%s, tag:%s id:%s\n", g.Value, g.Tag, g.Id)
}
type RuleList []Rule
func (g RuleList) String() string {
slist := make([]string, 0)
for _, v := range g {
slist = append(slist, v.String())
}
return strings.Join(slist, "")
}
func main() {
g1 := Rule{Value: "hello", Tag: "1", Id: "1"}
g2 := Rule{Value: "hello", Tag: "1", Id: "1"}
g3 := Rule{Value: "hello", Tag: "1", Id: "1"}
glist := RuleList{g1, g2, g3}
fmt.Println(glist)
}

You can achieve this by implementing Stringers interface [more about Stringers]
package main
import "fmt"
type Dummy struct{
Value, Tag, Id string
}
func (r Dummy) String() string {
return fmt.Sprintf("value:%v, tag:%v, id:%v\n", r.Value, r.Tag, r.Id)
}
func main(){
ls := make([]Dummy,0)
ls = append(ls, Dummy{"x0", "t0", "i0"})
ls = append(ls, Dummy{"x1", "t1", "i1"})
ls = append(ls, Dummy{"x2", "t2", "i2"})
ls = append(ls, Dummy{"x3", "t3", "i3"})
fmt.Println(Dummy{"x0", "t0", "i0"})
fmt.Println(ls)
}

Related

Converting Slice of custom type to slice of string

I have multiple structs like bellow
type Person struct {
first string
last string
age int
}
type Product struct {
ProductID int
Name string
Description string
Price float64
}
I want a function that will take a slice of any type as the first argument and a function as a second argument that it will use to call with each element of the slice to construct a string slice and will return that string slice. Something like map() in Typescript/scala or select() in C#.
Since Go doesn't have generics, "any type" can only mean interface{}. You can have something like this:
func ToStringSlice(arr []interface{}, convert func(interface{}) string) []string {
ret := []string{}
for _, elem := range arr {
ret = append(ret, convert(elem))
}
return ret
}
Then you can basically inject any conversion function you want. e.g.
fmt.Println(ToStringSlice([]interface{}{1, 2, "foo", "bar"},
func(x interface{}) string {
return fmt.Sprint(x)
}))
And since string conversions can go bad, I'd also add error checking:
// Define the function with an error return.
type Stringifier func(interface{}) (string, error)
func ToStringSlice(arr []interface{}, convert Stringifier) ([]string, error) {
ret := []string{}
for _, elem := range arr {
if s, e := convert(elem); e != nil {
return nil, e
} else {
ret = append(ret, s)
}
}
return ret, nil
}
one solution involves fmt.Stringer
package main
import (
"fmt"
)
func main() {
items := []fmt.Stringer{Product{ProductID: 1}, Person{first: "first"}}
var res []string
for _, i := range items {
res = append(res, i.String())
}
fmt.Println(res)
}
type Person struct {
first string
last string
age int
}
func (p Person) String() string { return p.first }
type Product struct {
ProductID int
Name string
Description string
Price float64
}
func (p Product) String() string { return fmt.Sprint(p.ProductID) }
That's a typical use case for an interface. Luckily enough the interface you need already exists as fmt.Stringer (https://golang.org/pkg/fmt/#Stringer):
Have a look at https://play.golang.org/p/d1sNPLKhNCU...
First your structs are required to implement the Stringer interface. This is done by simply adding a menthode String(), for example:
type Product struct {
ProductID int
Name string
Description string
Price float64
}
func (p Product) String() string {
return fmt.Sprintf("%d %s", p.ProductID, p.Name)
}
Now your Product is also a Stringer and can be Therefore be passed in any function that accepts Stringers:
func AsStringSlice(in []fmt.Stringer) []string {
out := []string{}
for _, i := range in {
out = append(out, i.String())
}
return out
}
Since "interface{} says nothing" (https://go-proverbs.github.io/) I would recommend to use interface{} as rarely as possible. This is achieved in this approach. Also you can avoid reflection that way.
On the other hand you have to be able to manage the structs, eg. implement the String function. If thats not possible because the Types come from a dependency consider wrapping them:
type MyTypeWithStringer package.ForeinTypeWithoutStringer
func(t MyTypeWithStringer) String() string {
// ...
}
This is a solution that fits me best
type Person struct {
first string
last string
age int
}
type Product struct {
ProductID int
Name string
Description string
Price float64
}
func mapToStringSlice(slice interface{}, mapFunc func(interface{}) string) []string {
s := reflect.ValueOf(slice)
if s.Kind() != reflect.Slice {
panic("mapToStringSlice() given a non-slice type")
}
ret := make([]string, s.Len())
for i:=0; i<s.Len(); i++ {
ret[i] = mapFunc(s.Index(i).Interface())
}
return ret
}
func main() {
persons := []Person{{
first: "A",
last: "g",
age: 20,
},{
first: "B",
last: "r",
age: 40,
},{
first: "C",
last: "",
age: 0,
}}
products := []Product{Product{ProductID: 1}, Product{ProductID: 2}}
personFirstNames := mapToStringSlice(persons, func(input interface{}) string {
return input.(Person).first
})
productIDs := mapToStringSlice(products, func(input interface{}) string {
return input.(Product).ProductID
})
}

Can I set tag dynamically on runtime? [duplicate]

I have the following:
package main
import (
"encoding/json"
"fmt"
"os"
"reflect"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
tag string `json:"-"`
Another
}
type Another struct {
Address string `json:"address"`
}
func (u *User) MarshalJSON() ([]byte, error) {
value := reflect.ValueOf(*u)
for i := 0; i < value.NumField(); i++ {
tag := value.Type().Field(i).Tag.Get("json")
field := value.Field(i)
fmt.Println(tag, field)
}
return json.Marshal(u)
}
func main() {
anoth := Another{"123 Jennings Street"}
_ = json.NewEncoder(os.Stdout).Encode(
&User{1, "Ken Jennings", "name",
anoth},
)
}
I am trying to json encode the struct but before I do I need to change the json key...eg the final json should look like:
{"id": 1, "name": "Ken Jennings", "address": "123 Jennings Street"}
I noticed the method for value.Type().Field(i).Tag.Get("json"), however there is no setter method. Why? and how do I get the desired json output.
Also, how do I iterate through all the fields, including the embedded struct Another?
https://play.golang.org/p/Qi8Jq_4W0t
Since Go 1.8 you can use this solution:
func main() {
anoth := Another{"123 Jennings Street"}
_ = json.NewEncoder(os.Stdout).Encode(
&User{1, "Ken Jennings", "name",
anoth},
)
}
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
tag string `json:"-"`
Another
}
type Another struct {
Address string `json:"address"`
}
func (u *User) MarshalJSON() ([]byte, error) {
type alias struct {
ID int64 `json:"id"`
Name string `json:"name"`
tag string `json:"-"`
Another
}
var a alias = alias(*u)
return json.Marshal(&a)
}
Will give us:
{"id":1,"name":"Ken Jennings","address":"123 Jennings Street"}
This solution made possible by the fact that in Go 1.8 you can assign structs with same structure but different tags to each other. As you see type alias has the same fields as type User but with different tags.
It's kludgy, but if you can wrap the struct in another, and use the new one for encoding, then you could:
Encode the original struct,
Decode it to an interface{} to get a map
Replace the map key
Then encode the map and return it
Thus:
type MyUser struct {
U User
}
func (u MyUser) MarshalJSON() ([]byte, error) {
// encode the original
m, _ := json.Marshal(u.U)
// decode it back to get a map
var a interface{}
json.Unmarshal(m, &a)
b := a.(map[string]interface{})
// Replace the map key
b["name"] = b["first"]
delete(b, "first")
// Return encoding of the map
return json.Marshal(b)
}
In the playground: https://play.golang.org/p/TabSga4i17
You can create a struct copy w/ new tag name by using reflect.StructOf and reflect.Value.Convert function
https://play.golang.org/p/zJ2GLreYpl0
func (u *User) MarshalJSON() ([]byte, error) {
value := reflect.ValueOf(*u)
t := value.Type()
sf := make([]reflect.StructField, 0)
for i := 0; i < t.NumField(); i++ {
fmt.Println(t.Field(i).Tag)
sf = append(sf, t.Field(i))
if t.Field(i).Name == "Name" {
sf[i].Tag = `json:"name"`
}
}
newType := reflect.StructOf(sf)
newValue := value.Convert(newType)
return json.Marshal(newValue.Interface())
}
It seems the tag property of type User in the question is meant to be used for renaming the JSON fieldname for the Name property.
An implementation of MarshalJSON with a bit of reflection can do the job without that additional tag property and also without an additional wrapper struct (as suggested in the accepted answer), like so:
package main
import (
"encoding/json"
"os"
"reflect"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
Another
}
type Another struct {
Address string `json:"address"`
}
// define the naming strategy
func (User) SetJSONname(jsonTag string) string {
if jsonTag == "first"{
return "name"
}
return jsonTag
}
// implement MarshalJSON for type User
func (u User) MarshalJSON() ([]byte, error) {
// specify the naming strategy here
return marshalJSON("SetJSONname", u)
}
// implement a general marshaler that takes a naming strategy
func marshalJSON(namingStrategy string, that interface{}) ([]byte, error) {
out := map[string]interface{}{}
t := reflect.TypeOf(that)
v := reflect.ValueOf(that)
fnctn := v.MethodByName(namingStrategy)
fname := func(params ...interface{}) string {
in := make([]reflect.Value, len(params))
for k, param := range params {
in[k] = reflect.ValueOf(param)
}
return fnctn.Call(in)[0].String()
}
outName := ""
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
switch n := f.Tag.Get("json"); n {
case "":
outName = f.Name
case "-":
outName = ""
default:
outName = fname(n)
}
if outName != "" {
out[outName] = v.Field(i).Interface()
}
}
return json.Marshal(out)
}
func main() {
anoth := Another{"123 Jennings Street"}
u := User{1, "Ken Jennings", anoth,}
e := json.NewEncoder(os.Stdout)
e.Encode(u)
}
This will print:
{"Another":{"address":"123 Jennings Street"},"id":1,"name":"Ken Jennings"}
However, be aware that MarshalJSON always sorts the JSON tags, while the standard encoder preserves the order of struct fields.

How do I dynamically change the struct's json tag?

I have the following:
package main
import (
"encoding/json"
"fmt"
"os"
"reflect"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
tag string `json:"-"`
Another
}
type Another struct {
Address string `json:"address"`
}
func (u *User) MarshalJSON() ([]byte, error) {
value := reflect.ValueOf(*u)
for i := 0; i < value.NumField(); i++ {
tag := value.Type().Field(i).Tag.Get("json")
field := value.Field(i)
fmt.Println(tag, field)
}
return json.Marshal(u)
}
func main() {
anoth := Another{"123 Jennings Street"}
_ = json.NewEncoder(os.Stdout).Encode(
&User{1, "Ken Jennings", "name",
anoth},
)
}
I am trying to json encode the struct but before I do I need to change the json key...eg the final json should look like:
{"id": 1, "name": "Ken Jennings", "address": "123 Jennings Street"}
I noticed the method for value.Type().Field(i).Tag.Get("json"), however there is no setter method. Why? and how do I get the desired json output.
Also, how do I iterate through all the fields, including the embedded struct Another?
https://play.golang.org/p/Qi8Jq_4W0t
Since Go 1.8 you can use this solution:
func main() {
anoth := Another{"123 Jennings Street"}
_ = json.NewEncoder(os.Stdout).Encode(
&User{1, "Ken Jennings", "name",
anoth},
)
}
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
tag string `json:"-"`
Another
}
type Another struct {
Address string `json:"address"`
}
func (u *User) MarshalJSON() ([]byte, error) {
type alias struct {
ID int64 `json:"id"`
Name string `json:"name"`
tag string `json:"-"`
Another
}
var a alias = alias(*u)
return json.Marshal(&a)
}
Will give us:
{"id":1,"name":"Ken Jennings","address":"123 Jennings Street"}
This solution made possible by the fact that in Go 1.8 you can assign structs with same structure but different tags to each other. As you see type alias has the same fields as type User but with different tags.
It's kludgy, but if you can wrap the struct in another, and use the new one for encoding, then you could:
Encode the original struct,
Decode it to an interface{} to get a map
Replace the map key
Then encode the map and return it
Thus:
type MyUser struct {
U User
}
func (u MyUser) MarshalJSON() ([]byte, error) {
// encode the original
m, _ := json.Marshal(u.U)
// decode it back to get a map
var a interface{}
json.Unmarshal(m, &a)
b := a.(map[string]interface{})
// Replace the map key
b["name"] = b["first"]
delete(b, "first")
// Return encoding of the map
return json.Marshal(b)
}
In the playground: https://play.golang.org/p/TabSga4i17
You can create a struct copy w/ new tag name by using reflect.StructOf and reflect.Value.Convert function
https://play.golang.org/p/zJ2GLreYpl0
func (u *User) MarshalJSON() ([]byte, error) {
value := reflect.ValueOf(*u)
t := value.Type()
sf := make([]reflect.StructField, 0)
for i := 0; i < t.NumField(); i++ {
fmt.Println(t.Field(i).Tag)
sf = append(sf, t.Field(i))
if t.Field(i).Name == "Name" {
sf[i].Tag = `json:"name"`
}
}
newType := reflect.StructOf(sf)
newValue := value.Convert(newType)
return json.Marshal(newValue.Interface())
}
It seems the tag property of type User in the question is meant to be used for renaming the JSON fieldname for the Name property.
An implementation of MarshalJSON with a bit of reflection can do the job without that additional tag property and also without an additional wrapper struct (as suggested in the accepted answer), like so:
package main
import (
"encoding/json"
"os"
"reflect"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
Another
}
type Another struct {
Address string `json:"address"`
}
// define the naming strategy
func (User) SetJSONname(jsonTag string) string {
if jsonTag == "first"{
return "name"
}
return jsonTag
}
// implement MarshalJSON for type User
func (u User) MarshalJSON() ([]byte, error) {
// specify the naming strategy here
return marshalJSON("SetJSONname", u)
}
// implement a general marshaler that takes a naming strategy
func marshalJSON(namingStrategy string, that interface{}) ([]byte, error) {
out := map[string]interface{}{}
t := reflect.TypeOf(that)
v := reflect.ValueOf(that)
fnctn := v.MethodByName(namingStrategy)
fname := func(params ...interface{}) string {
in := make([]reflect.Value, len(params))
for k, param := range params {
in[k] = reflect.ValueOf(param)
}
return fnctn.Call(in)[0].String()
}
outName := ""
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
switch n := f.Tag.Get("json"); n {
case "":
outName = f.Name
case "-":
outName = ""
default:
outName = fname(n)
}
if outName != "" {
out[outName] = v.Field(i).Interface()
}
}
return json.Marshal(out)
}
func main() {
anoth := Another{"123 Jennings Street"}
u := User{1, "Ken Jennings", anoth,}
e := json.NewEncoder(os.Stdout)
e.Encode(u)
}
This will print:
{"Another":{"address":"123 Jennings Street"},"id":1,"name":"Ken Jennings"}
However, be aware that MarshalJSON always sorts the JSON tags, while the standard encoder preserves the order of struct fields.

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))
}

How to recursively join a string to an array of type string in golang

I am struggling with this for quite some time. I basically want to create a function that recursively join a string to an array.
Like this :
join ", " ["one","two","three"]
should look like this "one, two, three"
There is already Join function in strings module. But it's not recursive, if you need recursive you can make it like this:
package main
import "fmt"
func join_helper(splitter string, arrOfStrings []string, res string) string {
if len(arrOfStrings) == 0 {
return res
}
if len(arrOfStrings) == 1 {
return join_helper(splitter, arrOfStrings[1:], res + arrOfStrings[0])
}
return join_helper(splitter, arrOfStrings[1:], res + arrOfStrings[0] + splitter)
}
func join(splitter string, arrOfStrings []string) string {
return join_helper(splitter, arrOfStrings, "")
}
func main(){
fmt.Println(join(",", []string{"a", "b", "c", "d"}))
}
Something like this
package main
import (
"fmt"
"strings"
)
func flatJoin(sep string, args ...interface{}) string {
values := make([]string, 0)
for _, item := range args {
switch v := item.(type) {
case string:
values = append(values, v)
case []string:
values = append(values, v...)
case fmt.Stringer:
values = append(values, v.String())
default:
values = append(values, fmt.Sprintf("%v", v))
}
}
return strings.Join(values, sep)
}
func main() {
fmt.Println(flatJoin(", ", "basic", "other", []string{"here", "more", "inner"}))
}
http://play.golang.org/p/yY6YnZZAak
This supports only one level of flattening, but you can customize the recursion on your switch statement depending on what you are expecting.

Resources