Conor
Conor

Reputation: 3579

Writing a struct's fields and values of different types to a file in Go

I'm writing a simple program that takes in input from a form, populates an instance of a struct with the received data and the writes this received data to a file.

I'm a bit stuck at the moment with figuring out the best way to iterate over the populated struct and write its contents to the file.

The struct in question contains 3 different types of fields (ints, strings, []strings).

I can iterate over them but I am unable to get their actual type.

Inspecting my posted code below with print statements reveals that each of their types is coming back as structs rather than the aforementioned string, int etc.

The desired output format is be plain text.

For example:

field_1="value_1"
field_2=10
field_3=["a", "b", "c"]

Anyone have any ideas? Perhaps I'm going about this the wrong way entirely?

func (c *Config) writeConfigToFile(file *os.File) {

    listVal := reflect.ValueOf(c)
    element := listVal.Elem()

    for i := 0; i < element.NumField(); i++ {
        field := element.Field(i)
        myType := reflect.TypeOf(field)

        if myType.Kind() == reflect.Int {
            file.Write(field.Bytes())
            } else {
                file.WriteString(field.String())
            }
        }
}

Upvotes: 0

Views: 1979

Answers (2)

eduncan911
eduncan911

Reputation: 17614

Why not use the built-in gob package to store your struct values?

I use it to store different structures, one per line, in files. During decoding, you can test the type conversion or provide a hint in a wrapper - whichever is faster for your given use case.

You'd treat each line as a buffer when Encoding and Decoding when reading back the line. You can even gzip/zlib/compress, encrypt/decrypt, etc the stream in real-time.

No point in re-inventing the wheel when you have a polished and armorall'd wheel already at your disposal.

Upvotes: 2

mkopriva
mkopriva

Reputation: 38303

Instead of using the Bytes method on reflect.Value which does not work as you initially intended, you can use either the strconv package or the fmt to format you fields.

Here's an example using fmt:

var s string
switch fi.Kind() {
case reflect.String:
    s = fmt.Sprintf("%q", fi.String())
case reflect.Int:
    s = fmt.Sprintf("%d", fi.Int())
case reflect.Slice:
    if fi.Type().Elem().Kind() != reflect.String {
        continue
    }

    s = "["
    for j := 0; j < fi.Len(); j++ {
        s = fmt.Sprintf("%s%q, ", s, fi.Index(i).String()) 
    }
    s = strings.TrimRight(s, ", ") + "]"
default:
    continue
}

sf := rv.Type().Field(i)
if _, err := fmt.Fprintf(file, "%s=%s\n", sf.Name, s); err!= nil {
    panic(err)
}

Playground: https://play.golang.org/p/KQF3CicVzA

Upvotes: 2

Related Questions