Go Struct JSON array of arrays

I´m having problems trying to mapping some data from specific Request Response because inside seriesLabels: there are data without property names (int,string) so I'm confused how to map that data:

This is the service response:

{
"data": {
    "seriesLabels": [
        [
            0,
            "(none)"
        ],
        [
            0,
            "Cerveza"
        ],
        [
            0,
            "Cigarros"
        ],
        [
            0,
            "Tecate"
        ],
        [
            0,
            "Cafe"
        ],
        [
            0,
            "Amstel"
        ],
        [
            0,
            "Leche"
        ],
        [
            0,
            "Ultra"
        ],
        [
            0,
            "Coca cola"
        ],
        [
            0,
            "Agua"
        ]
    ]
}
}

My go struct to map that info is:

type PopularWord struct {
    Data *Data `json:"data"`
}

type Data struct {
    SeriesLabels []*SeriesLabels `json:"seriesLabels"`
}

type SeriesLabels struct {
    value int32  `json:""`
    name  string `json:""`
}

What am I doing wrong? What is the correct way to declare the structs?

Upvotes: 0

Views: 1910

Answers (2)

Dietrich Epp
Dietrich Epp

Reputation: 213338

The problem is that SeriesLabels is represented in JSON as an array. If you want to use encoding/json, you have to implement the Unmarshaler interface to decode it (otherwise it will only accept JSON objects).

Fortunately, the code is simple:

func (s *SeriesLabels) UnmarshalJSON(d []byte) error {
    arr := []interface{}{&s.value, &s.name}
    return json.Unmarshal(d, &arr)
}

Note that this code ignores invalid input (array too long, or incorrect type). Depending on your needs, you may wish to add checks after the call to json.Unmarshal that the array length and contents (pointers) have not changed.

There are other JSON parsing libraries for Go that make this a bit less cumbersome, like gojay.

Upvotes: 2

icza
icza

Reputation: 417612

seriesLabels is a JSON array, and its elements are also JSON arrays.

To parse a JSON array of arrays, you may use a slice of slices in Go:

type Data struct {
    SeriesLabels [][]interface{}
}

Testing it:

var pw *PopularWord
if err := json.Unmarshal([]byte(src), &pw); err != nil {
    panic(err)
}
fmt.Println(pw)
for _, sl := range pw.Data.SeriesLabels {
    fmt.Println(sl)
}

Output (try it on the Go Playground):

&{0xc000120108}
[0 (none)]
[0 Cerveza]
[0 Cigarros]
[0 Tecate]
[0 Cafe]
[0 Amstel]
[0 Leche]
[0 Ultra]
[0 Coca cola]
[0 Agua]

To get the inner arrays as struct values, you may implement custom unmarshaling:

type Data struct {
    SeriesLabels []*SeriesLabels `json:"seriesLabels"`
}

type SeriesLabels struct {
    value int32 
    name  string
}

func (sl *SeriesLabels) UnmarshalJSON(p []byte) error {
    var s []interface{}
    if err := json.Unmarshal(p, &s); err != nil {
        return err
    }
    if len(s) > 0 {
        if f, ok := s[0].(float64); ok {
            sl.value = int32(f)
        }
    }

    if len(s) > 1 {
        if s, ok := s[1].(string); ok {
            sl.name = s
        }
    }
    return nil
}

Testing code is the same, output (try this one on the Go Playground):

&{0xc0000aa0f0}
&{0 (none)}
&{0 Cerveza}
&{0 Cigarros}
&{0 Tecate}
&{0 Cafe}
&{0 Amstel}
&{0 Leche}
&{0 Ultra}
&{0 Coca cola}
&{0 Agua}

Upvotes: 2

Related Questions