Search code examples
gopostman

How to pass and parse nested object with postman multipart/form-data


I have a route which works with multipart/form-data. All was good before I tried to pass nested objects via postman. There are a lot of code in this function but I removed it for this question because it does not matter.

func (h *handler) SubmitFormNewPark(w http.ResponseWriter, r *http.Request) {
    ctx := context.InitFromHTTP(w, r, "submit_form_new_park")
    defer ctx.OnExit(nil)

    err := r.ParseMultipartForm(0)
    if err != nil {
        logger.Get().Warn(ctx, "can't parse new park form", zap.Error(err))
        h.writeResponse(ctx, w, models.FormParkResponse{Success: false})
        return
    }

    multipartForm := make(map[string]interface{})
    for key, value := range r.MultipartForm.Value {
        multipartForm[key] = value
    }
}

Firstly all was good because I had not nested object

enter image description here

But after I added nested object there are different keys in r.MultipartForm.Value

enter image description here

If I print the r.MultipartForm.Value I get next map

map[city:[city] entity_name:[entity_name] ip_info[ip_address]:[ip_address] ip_info[ip_fio]:[ip_fio] name:[Name] office_address:[office address] office_phone:[74954954949]]

But I expect get "ip_info" key as map with keys ip_address and ip_fio

I also tried to use keys such as ip_info[0][ip_fio] and ip_info[0][ip_address].


Solution

  • You can parse nested objects with this code:

    Note: You can improove this example to recursively parse nested objects in nested objects

    package main
    
    import (
        "fmt"
        "log"
        "net/http"
        "regexp"
    )
    
    var rxNestedKey = regexp.MustCompile(`^(?P<key>[A-Za-z0-9_]+)\[(?P<n_key>[A-Za-z0-9_]+)\]$`)
    
    func Handler(w http.ResponseWriter, r *http.Request) {
        err := r.ParseMultipartForm(0)
        if err != nil {
    
            return
        }
    
        multipartForm := map[string]interface{}{}
        nestedBuf := map[string]map[string]interface{}{}
    
        for key, value := range r.MultipartForm.Value {
            // check key has nested key
            if rxNestedKey.MatchString(key) {
                // parse nested key (nK) and key (k)
                k := rxNestedKey.FindStringSubmatch(key)[1]
                nK := rxNestedKey.FindStringSubmatch(key)[2]
    
                if nestedBuf[k] == nil {
                    nestedBuf[k] = map[string]interface{}{}
                }
                nestedBuf[k][nK] = value
            } else {
                multipartForm[key] = value
            }
        }
    
        // collect all nested data from buff
        for k, v := range nestedBuf {
            multipartForm[k] = v
        }
    
        fmt.Printf("%+v\n", multipartForm)
    }
    
    func main() {
        http.HandleFunc("/", Handler)
        log.Fatal(http.ListenAndServe("localhost:8080", nil))
    }
    

    request

    Parsed as:

    map[ip:map[address:[127.0.0.1] port:[8080]] office_city:[Moscow] office_phone:[7845612349879]]