3

Suppose I define a struct as following:

type User struct {
    ID          string
    Name        string
    Age         uint
    City        string      `json:"address.city"`
    Province    string      `json:"address.province"`
}

I am able to take a User struct, and expand out the flattened fiends into a nested JSON structure, with an address object. I'm struggling however to go in the other direction.

How would I take the following JSON:

{
    "ID": "1",
    "Name": "Keith Baldwin",
    "Age": 30,
    "address": {
        "city": "Saskatoon",
        "province": "Saskatchewan"
    }
}

And unmarshal it into the given struct?

Is there something I'm missing, or will I just have to write it from scratch, probably using reflection?

Thanks

robbieperry22
  • 1,753
  • 1
  • 18
  • 49

3 Answers3

3

Create userInfo class

type UserInfo struct {
    ID      string `json:"ID"`
    Name    string `json:"Name"`
    Age     int    `json:"Age"`
    Address struct {
        City     string `json:"city"`
        Province string `json:"province"`
    } `json:"address"`
}

Then unmarshal your json data into a userinfo object

var userInfo UserInfo
    jsonStr := `{
    "ID": "1",
    "Name": "Keith Baldwin",
    "Age": 30,
    "address": {
        "city": "Saskatoon",
        "province": "Saskatchewan"
    }
}`
json.Unmarshal([]byte(jsonStr), &userInfo)
Ehsan.Saradar
  • 664
  • 6
  • 10
  • Here is the Go-playground example what @Ehsan.Saradar explained: https://play.golang.org/p/Bn_zAsF75JV – Abhay Kumar Nov 20 '18 at 05:28
  • Thanks for the note. Unfortunately this won't work for me, as I need to be able to do it for any arbitrary data structure, and only flatten fields defined in the struct as `json:"something.something"`. – robbieperry22 Nov 20 '18 at 17:34
2

I think you need create an other Address struct.

type Address struct {
    City     string `json:"city"`
    Province string `json:"province"`
}

type User struct {
    ID      string  `json:"id"`
    Name    string  `json:"name"`
    Age     uint    `json:"age"`
    Address Address `json:"address"`
}
KibGzr
  • 2,053
  • 14
  • 15
0

You could make a temporary struct type to dump that data into that matches the format you expect. e.g.

type userData struct {
    ID          string
    Name        string
    Age         uint
    address     map[string]string
}

Although my favorite thing to do is to just dump all of the json into a map with string keys and json.RawMessage as values. This will allow you to unmarshal all of the json into this map, and then unmarshal each individual field however you would want.

var allData map[string]json.RawMessage
var user User{}
//Assuming we first get our json from a request, otherwise use json.Unmarshal(jsonData, &allData)
err := json.NewDecoder(r.Body).Decode(&allData)
if err != nil {
    //handle
}
err = json.Unmarshal(allData["ID"], &c.ID)
if err != nil {
    //handle
}
var addressMap map[string]string
err = json.Unmarshal(allData["address"], addressMap)
if err != nil {
    //handle
}
c.City = addressMap["city"]
c.Province = addressMap["province"]

I haven't tested any of that code, so it may be error ridden, but it's just meant to convey the idea of converting all of the data into a map of json.RawMessage values and then unmarshalling each property. Normally you'd have a lot more error checking to see if values exist, etc. It's more work, for sure, but it's incredibly flexible and gives you a lot of visibility and power into how the data is handled as opposed to just unmarshalling into a struct and hoping the magic works.

Slotheroo
  • 925
  • 2
  • 9
  • 17