Home > Software design >  Decoding a dynamic field in Go
Decoding a dynamic field in Go

Time:01-20

I get a response from an external API that has a field which can have 2 values:

{"field": []}

or

{"field": {"key1": "value", "key2": "value"}}

I set the struct to be

type Object Struct {
   Field map[string]string `json:"field,omitempty"`
}

And then call my own implemented function to decode the response

func decode(response *http.Response) (*Object, error) {
    var response Object
    err := json.NewDecoder(response.Body).Decode(&response)
    if err != nil {
        return nil, err
    }

    return &response, nil
}

But this works only for the second response ( when field not empty is). For the first response I get an error.

CodePudding user response:

Rename the variable response inside decode function. Since both function argument & variable declared are of same name, it creates confusion.

CodePudding user response:

you can do a custom marshaler type for the Field. Example:

type keys struct {
        Key1 string
        Key2 string
}

type mytype struct {
        EmptySlice bool
        Keys       *keys
}

func (m *mytype) UnmarshalJSON(b []byte) error {
        if bytes.Equal(b, []byte("[]")) {
                m.EmptySlice = true
                return nil
        }

        m.Keys = &keys{}
        return json.Unmarshal(b, &m.Keys)
}

type Object struct {
        Field mytype `json:"field"`
}

func main() {
        input := []string{
                `{"field": []}`,
                `{"field": {"key1": "value", "key2": "value"}}`,
        }

        for i, s := range input {
                var o Object

                err := json.Unmarshal([]byte(s), &o)
                if err != nil {
                        log.Fatal(err)
                }

                fmt.Printf("%d: % v\n", i 1, o)
        }
}

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

CodePudding user response:

You can use a custom type and implement the UnmarshalJSON interface method on that type.

For example:

type Field struct {
    arr []string
    m   map[string]string
}

func (f *Field) UnmarshalJSON(b []byte) error {
    var m map[string]string
    err := json.Unmarshal(b, &m)
    if err == nil {
        f.m = m
        return nil
    }

    var arr []string
    err = json.Unmarshal(b, &arr)
    if err == nil {
        f.arr = arr
        return nil
    }

    return fmt.Errorf("type of property not array or map")
}

https://go.dev/play/p/JuFE--hWAjw

  •  Tags:  
  • Related