I have a file with many types of data record which I need to parse into structs.
I'd be grateful to learn of a idiomatic way -- if it exists -- of filling structs by record type. Something like python's namedtuple(*fields)
constructor.
package main
import (
"fmt"
"strconv"
"strings"
)
type X interface{}
type HDR struct {
typer, a string
b int
}
type BDY struct {
typer, c string
d int
e string
}
var lines string = `HDR~two~5
BDY~four~6~five`
func sn(s string) int {
i, _ := strconv.Atoi(s)
return i
}
func main() {
sl := strings.Split(lines, "\n")
for _, l := range sl {
fields := strings.Split(l, "~")
var r X
switch fields[0] {
case "HDR":
r = HDR{fields[0], fields[1], sn(fields[2])} // 1
case "BDY":
r = BDY{fields[0], fields[1], sn(fields[2]), fields[3]} // 2
}
fmt.Printf("%T : %v\n", r, r)
}
}
I'm specifically interested to learn if lines marked // 1
and // 2
can be conveniently replaced by code, perhaps some sort of generic decoder which allows the struct itself to handle type conversion.
Use the reflect package to programmatically set fields.
A field must be exported to be set by the reflect package. Export the names by uppercasing the first rune in the name:
type HDR struct {
Typer, A string
B int
}
type BDY struct {
Typer, C string
D int
E string
}
Create a map of names to the type associated with the name:
var types = map[string]reflect.Type{
"HDR": reflect.TypeOf((*HDR)(nil)).Elem(),
"BDY": reflect.TypeOf((*BDY)(nil)).Elem(),
}
For each line, create a value of the type using the types
map:
for _, l := range strings.Split(lines, "\n") {
fields := strings.Split(l, "~")
t := types[fields[0]]
v := reflect.New(t).Elem()
...
}
Loop over the fields in the line. Get the field value, convert the string to the kind of the field value and set the field value:
for i, f := range fields {
fv := v.Field(i)
switch fv.Type().Kind() {
case reflect.String:
fv.SetString(f)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n, _ := strconv.ParseInt(f, 10, fv.Type().Bits())
fv.SetInt(n)
}
}
This is a basic outline of the approach. Error handling is notabling missing: the application will panic if the type name is not one of the types mentioned in types
; the application ignores the error returned from parsing the integer; the application will panic if there are more fields in the data than the struct; the application does not report an error when it encounters an unsupported field kind; and more.