LiJung
LiJung

Reputation: 7969

How to provide string formatting for structs?

I've a struct called item

type Item struct {
    Limit int
    Skip int
    Fields string
}



item := Item {
            Limit: 3,
            Skip: 5,
            Fields: "Valuie",
    }

how could I get the field name, value and join it into a string.

something like:

item := Item {
            Limit: 3,
            Skip: 5,
            Fields: "Valuie",
    }

to a string something like

"Limit=3&Skip=5&Fields=Valuie"

And I've try reflections to get convert interface to field value map so far. Am I going the right way? Cause I think there might have some better solutions. And thanks!

m, _ = reflections.Items(data)
for k, v := range m {
    fmt.Printf("%s : %s\n", k, v)
}

I've got

Limit : %!s(int=3)
Skip : %!s(int=5)
Fields : Valuie

Upvotes: 5

Views: 9739

Answers (4)

zemirco
zemirco

Reputation: 16395

Take a look at go-querystring. It converts a struct into URL query parameters (your expected output).

type Item struct {
  Limit  int    `limit:"limit"`
  Skip   int    `url:"skip"`
  Fields string `url:"fields"`
}

item := Item {
  Limit: 3,
  Skip: 5,
  Fields: "Valuie",
}

v, _ := query.Values(item)
fmt.Print(v.Encode()) 
// will output: "limit=3&skip=5&fields=Valuie"

Upvotes: 0

Paul Hankin
Paul Hankin

Reputation: 58369

There's no need to use reflection for this. Just write out the code for the types you have.

func (i *Item) URLValues() string {
    uv := url.Values()
    uv.Set("Limit", strconv.Itoa(i.Limit))
    uv.Set("Skip", strconv.Itoa(i.Skip))
    uv.Set("Fields", i.Fields)
    return uv.Encode()
}

This code is simple, readable, and you don't need to think to write it. Unless you have a lot of types that you're going to be converting to values then I'd not even think about the magic reflection-based solution to this problem.

Upvotes: 3

nemo
nemo

Reputation: 57737

For any struct you can use reflection and url.Values from the net/url package:

i := Item{1, 2, "foo"}

v := url.Values{}
ri := reflect.ValueOf(i)

ri.FieldByNameFunc(func(name string) bool {
    v.Set(name, fmt.Sprintf("%v", ri.FieldByName(name).Interface()))
    return false
})

fmt.Println(v.Encode())

Example on play.

Of course, this code does not handle nested data structures or slices so you would need to extend the code if you use other data structures to make it more general. However, this example should get you started.

Upvotes: 1

creack
creack

Reputation: 121792

You can use %v instead of %s. %s will assume a string, something that can be converted to a string (i.e. byte array) or an object with a String() method. Using %v will check the type and display it correctly.

Example of the String() method call with %s with your example: http://play.golang.org/p/bxE91IaVKj

Upvotes: 10

Related Questions