jsongostructtypesgo-map

how to convert map[string]interface{} data to struct


I do not know how to ask, so i will ask with an example.

I have some data like that

{
..
    "velocityStatEntries":  {

    "8753":  {

        "estimated":  {"value":  23.0,"text":  "23.0"},

        "completed":  {"value":  27.0,"text":  "27.0"}
    },

    "8673":  {

        "estimated":  {"value":  54.5,"text":  "54.5"},

        "completed":  {"value":  58.5,"text":  "58.5"}
    },
    .
    .
    .

    }
..
}

I want to declare a type that takes map key to its "KEY" or any property that is given by me. Is it possible without using map iteration?

Expected output:

{...
   "velocityStatEntries": {
   {
     "key": "8753",
     "estimated":  {"value":  54.5,"text":  "54.5"},
     "completed":  {"value":  58.5,"text":  "58.5"}
   },
   {
     "key": "8673",
     "estimated":  {"value":  54.5,"text":  "54.5"},
     "completed":  {"value":  58.5,"text":  "58.5"}
   },
  }
...
}

This is what i have done

type VelocityStatEntry struct {
    Key string
    Estimated struct {
        Value float64 `json:"value"`
        Text  string  `json:"text"`
    } `json:"estimated"`
    Completed struct {
        Value float64 `json:"value"`
        Text  string  `json:"text"`
    } `json:"completed"`
}

type RapidChartResponse struct {
    ...
    VelocityStatEntries map[string]VelocityStatEntry `json:"velocityStatEntries"`
    ..
}

But it is not working. I want to take that string map key to KEY property.


Solution

  • If the data originates from JSON then you should skip the map[string]interface{} and instead use a custom unmarshaler implemented by your desired struct that does what you want. Perhaps by utilizing a map[string]json.RawMessage. But map[string]interface{} to struct conversion is a pain, avoid it if possible.

    For example:

    type VelocityStatEntryList []*VelocityStatEntry
    
    func (ls *VelocityStatEntryList) UnmarshalJSON(data []byte) error {
        var m map[string]json.RawMessage
        if err := json.Unmarshal(data, &m); err != nil {
            return err
        }
        for k, v := range m {
            e := &VelocityStatEntry{Key: k}
            if err := json.Unmarshal([]byte(v), e); err != nil {
                return err
            }
            *ls = append(*ls, e)
        }
        return nil
    }
    

    https://go.dev/play/p/VcaW_BWXRVr