David Fulton
David Fulton

Reputation: 767

Golang - create an object of the same type as passed

I'm trying to build a generic function which will parse input (in JSON) into a specified structure. The structure may vary at run-time, based on parameters which are passed to the function. I'm currently trying to achieve this by passing an object of the right type and using reflect.New() to create a new output object of the same type.

I'm then parsing the JSON into this object, and scanning the fields.

If I create the object and specify the type in code, everything works. If I pass an object and try to create a replica, I get an "invalid indirect" error a few steps down (see code).

import (
    "fmt"
    "reflect"
    "encoding/json"
    "strings"
)

type Test struct {
    FirstName   *string `json:"FirstName"`
    LastName    *string `json:"LastName"`
}

func genericParser(incomingData *strings.Reader, inputStructure interface{}) (interface{}, error) {
    //******* Use the line below and things work *******
    //parsedInput := new(Test)


    //******* Use vvv the line below and things don't work *******
    parsedInput := reflect.New(reflect.TypeOf(inputStructure))

    decoder := json.NewDecoder(incomingData)
    err := decoder.Decode(&parsedInput)
    if err != nil {
        //parsing error
        return nil, err
    }

    //******* This is the line that generates the error "invalid indirect of parsedInput (type reflect.Value)" *******
    contentValues := reflect.ValueOf(*parsedInput)
    for i := 0; i < contentValues.NumField(); i++ {
        //do stuff with each field
        fmt.Printf("Field name was: %s\n", reflect.TypeOf(parsedInput).Elem().Field(i).Name)
    }
    return parsedInput, nil
}


func main() {
    inputData := strings.NewReader("{\"FirstName\":\"John\", \"LastName\":\"Smith\"}")
    exampleObject := new(Test)
    processedData, err := genericParser(inputData, exampleObject)
    if err != nil {
        fmt.Println("Parsing error")
    } else {
        fmt.Printf("Success: %v", processedData)
    }
}

If I can't create a replica of the object, then a way of updating / returning the one supplied would be feasible. The key thing is that this function must be completely agnostic to the different structures available.

Upvotes: 4

Views: 1929

Answers (1)

Mr_Pink
Mr_Pink

Reputation: 109443

reflect.New isn't a direct analog to new, as it can't return a specific type, it only can return a reflect.Value. This means that you are attempting to unmarshal into a *reflect.Value, which obviously isn't going to work (even if it did, your code would have passed in **Type, which isn't what you want either).

Use parsedInput.Interface() to get the underlying value after creating the new value to unmarshal into. You then don't need to reflect on the same value a second time, as that would be a reflect.Value of a reflect.Value, which again isn't going to do anything useful.

Finally, you need to use parsedInput.Interface() before you return, otherwise you are returning the reflect.Value rather than the value of the input type.

For example:

func genericParser(incomingData io.Reader, inputStructure interface{}) (interface{}, error) {
    parsedInput := reflect.New(reflect.TypeOf(inputStructure).Elem())

    decoder := json.NewDecoder(incomingData)
    err := decoder.Decode(parsedInput.Interface())
    if err != nil {
        return nil, err
    }

    for i := 0; i < parsedInput.Elem().NumField(); i++ {
        fmt.Printf("Field name was: %s\n", parsedInput.Type().Elem().Field(i).Name)
    }
    return parsedInput.Interface(), nil
}

https://play.golang.org/p/CzDrj6sgQNt

Upvotes: 2

Related Questions