将数组解组为struct

I'm trying to figure out how I can (using gin) create a struct from an api call

"icon": [
        "https://api.figo.me/assets/images/accounts/postbank.png",
        {
          "48x48": "https://api.figo.me/assets/images/accounts/postbank_48.png",
          "60x60": "https://api.figo.me/assets/images/accounts/postbank_60.png",
          "72x72": "https://api.figo.me/assets/images/accounts/postbank_72.png",
          "84x84": "https://api.figo.me/assets/images/accounts/postbank_84.png",
          "96x96": "https://api.figo.me/assets/images/accounts/postbank_96.png",
          "120x120": "https://api.figo.me/assets/images/accounts/postbank_120.png",
          "144x144": "https://api.figo.me/assets/images/accounts/postbank_144.png",
          "192x192": "https://api.figo.me/assets/images/accounts/postbank_192.png",
          "256x256": "https://api.figo.me/assets/images/accounts/postbank_256.png"
        }
      ],

into

type CatalogBank struct {
        Advice      string `json:"advice"`
        BankCode    string `json:"bank_code"`
        BankName    string `json:"bank_name"`
        BIC         string `json:"bic"`
        Credentials []struct {
            Label  string `json:"label"`
            Masked bool   `json:"masked"`
        } `json:"credentials"`
        Icon     []struct {

        } `json:"icon"`
        Language []byte `json:"language"`
    }

The icon part is just an extract from, but I always get an unmarshall error for this part. How would I have to definde the 'Icon' part in the struct?

This would work

package main

type CatalogBank struct {
   Icon []interface{} `json:"icon"`
}

This is a little tricky in Golang because of the non-strict type in the JSON. If that is definitely the format you are going to receive the data in, you should unmarshal to an Interface{} and then parse the interface into a struct that you can use in your Golang

Direct Unmarshalling cannot be done, as the type of each field is not known

type Icon struct{
    ImageLink string
    ImageLink48 string
    // ...
}

type CatalogBank struct {
    Advice      string `json:"advice"`
    IconRaw     []interface{} `json:"icon"`
    Icon        []Icon
    //...
}

func UnmarshalIcon(c &CatalogBank, i interface{}):
    // first convert it to the top level list
    newIcon := Icon{}
    listOfIcons := i.([]interface{})
    for _, i := range listOfIcons:
        switch iT := i.(type) {
           case string:
                newIcon.ImageLink = iT
           case map[string]interface{}:
              for smallIconsKey, smallIconLink := range iT {
                  if smallIconsKey == "48x48"{
                         newIcon.ImageLink48 = smallIconLink.(string)
                  }
                  // and so on
        }


var c CatalogBank{}
_ := json.Unmarshal([]byte(your_json), &c)
for _, i := range c.IconRaw:
   UnmarshalIcon(&c, i)

Caveat Emptor: I haven't checked above implementation but it should be something like this

You can not use []struct {} for icon, change it to []interface{} instead, or if you want operate on type safe type look at the second solution with cusom unmarshaler

Solution 1

    package main

    import (
        "encoding/json"
        "fmt"
        "log"
    )

    type CatalogBank struct {
        Advice   string `json:"advice"`
        BankCode string `json:"bank_code"`
        BankName string `json:"bank_name"`
        BIC      string `json:"bic"`
        Credentials []struct {
            Label  string `json:"label"`
            Masked bool   `json:"masked"`
        } `json:"credentials"`
        Icon []interface{} `json:"icon"`
        Language []byte `json:"language"`
    }

    func main() {

        data := `
    {
    "Advice":"abc",
    "icon": [
            "https://api.figo.me/assets/images/accounts/postbank.png",
            {
              "48x48": "https://api.figo.me/assets/images/accounts/postbank_48.png",
              "60x60": "https://api.figo.me/assets/images/accounts/postbank_60.png",
              "72x72": "https://api.figo.me/assets/images/accounts/postbank_72.png",
              "84x84": "https://api.figo.me/assets/images/accounts/postbank_84.png",
              "96x96": "https://api.figo.me/assets/images/accounts/postbank_96.png",
              "120x120": "https://api.figo.me/assets/images/accounts/postbank_120.png",
              "144x144": "https://api.figo.me/assets/images/accounts/postbank_144.png",
              "192x192": "https://api.figo.me/assets/images/accounts/postbank_192.png",
              "256x256": "https://api.figo.me/assets/images/accounts/postbank_256.png"
            }
          ]
    }
    `

        bank := &CatalogBank{}

        err := json.Unmarshal([]byte(data), bank)
        if err != nil {
            log.Fatal(err)
        }
        for _, icon := range bank.Icon {
            fmt.Printf(" %v
 ", icon)
        }

    }

Solution 2:

    package main

    import (
        "encoding/json"
        "fmt"
        "log"
    )

    type Icons struct {
        URL    string
        BySize map[string]string
    }



    type CatalogBank struct {
        Advice   string `json:"advice"`
        BankCode string `json:"bank_code"`
        BankName string `json:"bank_name"`
        BIC      string `json:"bic"`
        Credentials []struct {
            Label  string `json:"label"`
            Masked bool   `json:"masked"`
        } `json:"credentials"`
        Icon     *Icons `json:"-,"`
        Language []byte `json:"language"`
    }

    func (p *CatalogBank) Unmarshal(data []byte) error {


        type Transient struct {
            *CatalogBank
            Icon []interface{} `json:"icon"`
        }

        var transient = &Transient{CatalogBank:p}
        err := json.Unmarshal([]byte(data), transient)
        if err != nil {
            return err
        }
        p.Icon = &Icons{
            BySize: make(map[string]string),
        }

        if len(transient.Icon) > 0 {
            if url, ok := transient.Icon[0].(string); ok {
                p.Icon.URL = url
            }
            if aMap, ok := transient.Icon[1].(map[string]interface{}); ok {
                for k, v := range aMap {
                    p.Icon.BySize[k] = v.(string)
                }
            }
        }
        return nil
    }

    func main() {

        data := `
            {
            "Advice":"abc",
            "icon": [
                    "https://api.figo.me/assets/images/accounts/postbank.png",
                    {
                      "48x48": "https://api.figo.me/assets/images/accounts/postbank_48.png",
                      "60x60": "https://api.figo.me/assets/images/accounts/postbank_60.png",
                      "72x72": "https://api.figo.me/assets/images/accounts/postbank_72.png",
                      "84x84": "https://api.figo.me/assets/images/accounts/postbank_84.png",
                      "96x96": "https://api.figo.me/assets/images/accounts/postbank_96.png",
                      "120x120": "https://api.figo.me/assets/images/accounts/postbank_120.png",
                      "144x144": "https://api.figo.me/assets/images/accounts/postbank_144.png",
                      "192x192": "https://api.figo.me/assets/images/accounts/postbank_192.png",
                      "256x256": "https://api.figo.me/assets/images/accounts/postbank_256.png"
                    }
                  ]
            }
            `

        bank := &CatalogBank{}
        err := bank.Unmarshal([]byte(data))
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("advice: %v
", bank.Advice)

        fmt.Printf("icon: %v
", bank.Icon.URL)
        for size, icon := range bank.Icon.BySize {
            fmt.Printf("%v =>  %v
 ",size, icon)
        }
    }

You can define your icon like this:

package main

import (
    "fmt"
    "encoding/json"
)

var testIcon = []byte(`{"icon":[
                        "https://api.figo.me/assets/images/accounts/postbank.png",
                        {
                            "48x48":"https://api.figo.me/assets/images/accounts/postbank_48.png"
                        }]
                    }`)

func main() {

    icon := make(map[string][]interface{})
    err := json.Unmarshal(testIcon, &icon)
    if err != nil {
        panic(err)
    }

    fmt.Println(icon)
    // map[icon:[https://api.figo.me/assets/images/accounts/postbank.png map[48x48:https://api.figo.me/assets/images/accounts/postbank_48.png]]]

}