Norio Akagi Norio Akagi - 5 months ago 11
JSON Question

What is the best way to handle "a single element or an array" JSON property in Go?

Now we have a JSON HTTP request data which would be a single element like

{"data": {"id":1}}
OR an array of elements like
{"data": [{"id":1}, {"id":2}]}
.

Since a client side cannot change the implementation, we have to keep and accept this data structure.

Currently I implement struct like:

type Request struct {
rawData json.RawMessage `json:"data"`

Data *Data `json:"-"`
DataList []*Data `json:"-"`
}


And first parse "data" property as json.RawMessage into variable
req
, try parsing as a single element first, then if it fails try parsing as an array.

if err := json.Unmarshal(req.rawData, &req.Data); err != nil {
if err := json.Unmarshal(req.RawData, &req.DataList); err != nil {
return errors.New("could not parse data")
} else if len(req.DataList) < 1 {
return errors.New("empty list")
}
}


In this case, later when we want to use this data, we can check if
rawData
is a single element or an array by
len(req.Datalist) >= 1
. (Or I can set some flag to show that like
isDataMultiple
in the struct, when parsing).

Is there any idiomatic way to achieve the same result in Go other than what I'm doing?

Answer

Here's how I would do it:

type Request struct {
   RawData json.RawMessage `json:"data"  // export field for unmarshal of entire request
   DataList []*Data `json:"-"`           // always use slice
}

// Is it an array?
if bytes.HasPrefix(bytes.TrimSpace(req.RawData), []byte{'['})) {
   if err := json.Unmarshal(req.RawData, &req.DataList); err != nil {
       // handle error parsing array
   }
} else {
   var v Data
   if err := json.Unmarshal(req.RawData, &v); err != nil {
       // handle error parsing single value
   }
   req.DataList = []*Data{&v)
}
Comments