How to make interface slice and append value

Issue

I want to do a function to parse a slice (an abstract parsing method, which can be JSON, msgpack or XML) from a string array. The type of this slice is uncertain, which can be [] A or [] * A. but now I have a problem. Can you help me

func unmarshalSlice(strings []string, to interface{}) (err error) {
    elementType := reflect.TypeOf(to).Elem()
    to = reflect.MakeSlice(reflect.SliceOf(elementType), 0, len(strings))
    for _, str := range strings {
        value := reflect.New(elementType).Interface()
        if err = json.Unmarshal([]byte(str), value); nil != err {
            return
        }
        to = reflect.Append(to.(reflect.Value), reflect.ValueOf(value).Elem())
    }
    

    return
}

the test code is

func TestUnmarshalSlice(t *testing.T) {
    var members []*Member
    _ = unmarshalSlice([]string{
        `{
"Value": "store"
}`, `{
"Value": "zhang"
}`,
    }, members)
    fmt.Println(members)
}

Solution

I have corrected some lines in your unmarshalSlice function.

First you have to create the slice of sent type and then append those type values to that slice. And at the end assign that slice as the value of sent assignable interface.

And call the unmarshalSlice function with assignable type.

type Member struct {
    Value string
}
func TestUnmarshalSlice(t *testing.T) {
    var members []Member
    _ = unmarshalSlice([]string{
        `{
"Value": "store"
}`, `{
"Value": "zhang"
}`,
    }, &members)
    fmt.Println(members[0], members[1]) //Output: {store} {zhang}
}

func TestUnmarshalPointerSlice(t *testing.T) {
    var members []*Member
    _ = unmarshalSlice([]string{
        `{
"Value": "store"
}`, `{
"Value": "zhang"
}`,
    }, &members)
    fmt.Println(members[0], members[1]) //Output: &{store} &{zhang}
}

func unmarshalSlice(strings []string, to interface{}) (err error) {
    elementType := reflect.TypeOf(to).Elem().Elem()
    // create slice of *Member{}
    o := reflect.MakeSlice(reflect.TypeOf(to).Elem(), 0, len(strings))
    for _, str := range strings {
        // create empty assignable Member{}
        value := reflect.New(elementType).Interface()
        if err = json.Unmarshal([]byte(str), value); nil != err {
            return
        }
        // append *Member{} to slice
        o = reflect.Append(o, reflect.ValueOf(value).Elem())
    }

    // set created slice to the value of "to"
    reflect.ValueOf(to).Elem().Set(o)

    return
}

you can run test here

Answered By – nipuna

Answer Checked By – David Marino (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.