2

Here is schematic example what I need:

func decode(data []byte, target interface{}) (interface{}, error) {
    decoded := target.(reflect.TypeOf(target)) // pseudocode
    err := json.Unmarshal(data, &decoded)
    ...

I found several similar questions on SO, but all solutions are about switch by target.(type). This is not the best solution what I look for.

Also I found solution with reflection:

decoded := reflect.ValueOf(target).Convert(reflect.TypeOf(target))
// fmt.Printf("%T", decoded) == reflect.Value

But I couldn't understand how to get struct from reflect.Value to pass it to json.Unmarshal function.

How this decode function will be used?

I have multiple requests in different structures. I can determine what struct I should use to decode request. I have mapping between request type and structure like this map[RequestMethod]interface{}.

Schematic version looks like this:

func hydrate(data []byte) (interface{}, error) {
    var base baseResponse

    if err := json.Unmarshal(data, &base); err != nil {
        return nil, err
    }

    target, ok := methodsMap[Method(base.Method)]

    if !ok {
        return nil, errors.New("Trying to hydrate data with unknown method: " + base.Method)
    }

    decoded, err := decode(data, target) // Expected target type.

Added:

If we pass our target to json.Unmarshal without casting to it type we will obtain map. Example:

func decode(data []byte, target interface{}) (interface{}, error) {
    err := json.Unmarshal(data, &target)
    // fmt.Printf("%T", target) == map[string]interface{} not struct.

Solution

Thanks to @icza we found solution:

func hydrate(data []byte) (interface{}, error) {
    var base baseResponse

    if err := json.Unmarshal(data, &base); err != nil {
        return nil, err
    }

    target, ok := methodsMap[Method(base.Method)]

    if !ok {
        return nil, errors.New("Trying to hydrate data with unknown method: " + base.Method)
    }

    // Clone request draft struct.
    decoded := reflect.New(reflect.ValueOf(target).Type()).Interface()
    err := decode(data, decoded)
    ...

Related links:

Golang interface{} type misunderstanding

How to copy an interface value in Go?

dlsniper
  • 7,188
  • 1
  • 35
  • 44
  • 1
    This seems very unnecessary. Simply passing `target` to `json.Unmarshal` should work fine for you. –  Nov 30 '16 at 12:26
  • @TimCooper, if we pass `target` with type `interface{}` to `json.Unmarshal` we will obtain `map[string]interface{}`. – Alexander Borisov Nov 30 '16 at 12:28
  • 4
    Show us how you call this `decode()` function. Most likely you pass a wrong value. – icza Nov 30 '16 at 12:36
  • @icza added usage example to question. – Alexander Borisov Nov 30 '16 at 12:44
  • 1
    Possible duplicate of [Passing values to interface{}](http://stackoverflow.com/a/35449654/1705598); and [Golang interface{} type misunderstanding](http://stackoverflow.com/questions/38829941/golang-interface-type-misunderstanding/38830638#38830638). – icza Nov 30 '16 at 12:48
  • @icza this question is very similar to my use case but I understand why I obtain `map` instead of `struct`. So my question is about casting not unmarshaling. – Alexander Borisov Nov 30 '16 at 12:58
  • 3
    @AlexBor If you read my other answers: you don't need to convert it to anything. Where you go wrong is calling your `decode()` function: you must pass a pointer to a struct to it. Inside `decode()`, you just have to pass the `interface{}` value to `json.Unmarshal()`. – icza Nov 30 '16 at 12:59
  • @icza I can't pass pointer to `methodsMap[Method(base.Method)]` struct because it contains draft struct for this request. I'm searching for solution to create a copy for this struct. Then I will be able to send its pointer to `decode()`. After all I will update my question. If you know better way for duplicating structure (creating new instance with the same type) - I will be very grateful for the advice. – Alexander Borisov Nov 30 '16 at 13:07
  • 1
    @AlexBor To copy a value wrapped in an interface: [How to copy an interface value in Go?](http://stackoverflow.com/questions/37851500/how-to-copy-an-interface-value-in-go/37851764#37851764) – icza Nov 30 '16 at 13:09
  • @icza in your answer you use casting to specified type `reflect.New(...).Interface().(User)`. In my question I ask how I can specify type without hardcoding it? – Alexander Borisov Nov 30 '16 at 13:14
  • 2
    @AlexBor Just leave out the final type assertion. I used that there because I worked with it afterwards. Just use `reflect.New(...).Interface()`. – icza Nov 30 '16 at 13:16
  • @icza Thank you! It works. – Alexander Borisov Nov 30 '16 at 13:49

1 Answers1

5

Use the following code to create a copy of the draft struct and unmarshal to it:

t := reflect.TypeOf(methodsMap[Method(base.Method)])
pv := reflect.New(t)
err := json.Unmarshal(p, pv.Interface())
return pv.Elem().Interface(), err  // pv.Elem() dereferences ptr 

playground example

Charlie Tumahai
  • 113,709
  • 12
  • 249
  • 242
  • you are a wonderful human being -- tried 'all the combinations' but this one, and was banging my head against the wall. Thank you thank you thank you! – pyInTheSky Jun 18 '21 at 21:40