Search code examples
jsongoreflectionunmarshallingreflect

JSON Unmarshal does not work with dynamic struct created by relect


I'm trying to parse JSON files by using dynamically created structs, but apparently I'm doing something wrong. Can somebody please tell we what am I doing wrong here:

structured := make(map[string][]reflect.StructField)
structured["Amqp1"] = []reflect.StructField{
    reflect.StructField{
        Name: "Test",
        Type: reflect.TypeOf(""),
        Tag:  reflect.StructTag(`json:"test"`),
    },
    reflect.StructField{
        Name: "Float",
        Type: reflect.TypeOf(5.5),
        Tag:  reflect.StructTag(`json:"float"`),
    },
    reflect.StructField{
        Name: "Connections",
        Type: reflect.TypeOf([]Connection{}),
        Tag:  reflect.StructTag(`json:"connections"`),
    },
}

sections := []reflect.StructField{}
for sect, params := range structured {
    sections = append(sections,
        reflect.StructField{
            Name: sect,
            Type: reflect.StructOf(params),
        },
    )
}

parsed := reflect.New(reflect.StructOf(sections)).Elem()
if err := json.Unmarshal([]byte(JSONConfigContent), &parsed); err != nil {
    fmt.Printf("unable to parse data from provided configuration file: %s\n", err)
    os.Exit(1)
}

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

Thanks in advance.


Solution

  • You want to use .Interface() to return the actual, underlying value, which should be a pointer to the concrete anonymous struct.

    Note that the reflect.New function returns a reflect.Value representing a pointer to a new zero value for the specified type. The Interface method, in this case, returns that pointer as interface{} which is all you need for json.Unmarshal.

    If, after unmarshaling, you need a non-pointer of the struct you can turn to reflect again and use reflect.ValueOf(parsed).Elem().Interface() to effectively dereference the pointer.

    parsed := reflect.New(reflect.StructOf(sections)).Interface()
    if err := json.Unmarshal([]byte(JSONConfigContent), parsed); err != nil {
        fmt.Printf("unable to parse data from provided configuration file: %s\n", err)
        os.Exit(1)
    }
    

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