This question already has an answer here:
I have a struct representing a dataset that I need to write to a CSV file as a time-series data. This is what I have so far.
type DataFields struct {
Field1 int,
Field2 string,
...
Fieldn int
}
func (d DataFields) String() string {
return fmt.Sprintf("%v,%v,...,%v", Field1, Field2,..., Fieldn)
}
Is there a way I can iterate through the members of the struct and construct a string object using it?
Performance is not really an issue here and I was wondering if there was a way I could generate the string without having to modify the String()
function if the structure changed in the future.
EDITED to add my change below:
This is what I ended up with after looking at the answers below.
func (d DataFields) String() string {
v := reflect.ValueOf(d)
var csvString string
for i := 0; i < v.NumField(); i++ {
csvString = fmt.Sprintf("%v%v,", csvString, v.Field(i).Interface())
}
return csvString
}
</div>
What you are looking for is called reflection. This answer explains how to use it to loop though a struct and get the values.
This is the example the author uses on the other answer:
package main
import (
"fmt"
"reflect"
)
func main() {
x := struct{Foo string; Bar int }{"foo", 2}
v := reflect.ValueOf(x)
values := make([]interface{}, v.NumField())
for i := 0; i < v.NumField(); i++ {
values[i] = v.Field(i).Interface()
}
fmt.Println(values)
}
You can see it working on the go playground.
One way would be to use the reflect
package. There is a Value.Field(int) Value
method that might be usefull to you. You would essentially first call ValueOf(interface{}) Value
with your DataFields, and then have a simple loop calling Field(int) Value
on the Value
.
Another approach is to use code generation which would generate a serializer code for you.
The trade-offs are:
Codegen is more compilcated in that it, most of the time, relies on running an external program (though this could be made simpler by employing go run
as it's supposed to be always available).
Every time you make a change to your data type adding or removing a field which has to be serialized, you need to run go generate
to regenerate the serializer code.
On the flip side the resulting code is fast and robust, and the changes to the data type are usually seldom enough.
Reflection is simpler in that it does not require thinking about regenerating the code.
On the flip side, the code which uses reflect
is usually ugly and quite hard to understand. And of course it incurs runtime performance penalty.