resty
resty copied to clipboard
SetResult replaces nested struct with a map inside a map
In the provided sample code, after a successful HTTP request, result["data"] changes type from getIP to map[string]interface{}. I don't know if this is feature or a bug, but it is unexpected and annoying to use. This does not happen if the outer map is replaced with a struct.
package main
import (
"encoding/json"
"log"
"net/http"
"net/netip"
"github.com/davecgh/go-spew/spew"
"github.com/go-resty/resty/v2"
)
type getIP struct {
Address string `json:"address"`
Version int `json:"version"`
}
const serveAddr = ":48916"
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
addr := netip.MustParseAddrPort(r.RemoteAddr).Addr().String()
result := map[string]any{
"data": getIP{
Address: addr,
Version: 4,
},
}
enc := json.NewEncoder(w)
if err := enc.Encode(result); err != nil {
log.Fatalln(err)
}
})
go func() {
if err := http.ListenAndServe(serveAddr, nil); err != http.ErrServerClosed {
log.Fatalf("Error starting server: %v\n", err)
}
}()
result := map[string]any{
"data": getIP{},
}
spew.Dump(result)
r := resty.New().R().SetResult(&result)
httpResp, err := r.Get("http://localhost" + serveAddr)
if err != nil {
log.Fatalf("unable to contact service: %v\n", err)
}
if httpResp.IsError() {
log.Fatalf("error contacting service: %s\n", httpResp.Status())
}
spew.Dump(result)
}
Hi @maticmeznar
I trace the result
is depends on the JSONUnmarshaler
.
The resty client are set the default of JSONUnmarshaler using "encoding/json".
To unmarshal JSON into an interface value, Unmarshal stores one of these in the interface value:
...
map[string]interface{}, for JSON objects
...
Refer to the official documentation on Unmarshal() for more information.
Reproduce
To make this clarify we tried to reproduce.
- Reproduce without the json unmarshal.
...
r := resty.New().SetJSONUnmarshaler(func(data []byte, v interface{}) error {
return nil
}).R().SetResult(&result)
...
Dump result without json unmarshal:
(map[string]interface {}) (len=1) {
(string) (len=4) "data": (main.getIP) {
Address: (string) "",
Version: (int) 0
}
}
(map[string]interface {}) (len=1) {
(string) (len=4) "data": (main.getIP) {
Address: (string) "",
Version: (int) 0
}
}
- Reproduce with the json unmarshal.
...
r := resty.New().SetJSONUnmarshaler(func(data []byte, v interface{}) error {
return json.Unmarshal(data, &v)
}).R().SetResult(&result)
...
Dump result with json unmarshal:
(map[string]interface {}) (len=1) {
(string) (len=4) "data": (main.getIP) {
Address: (string) "",
Version: (int) 0
}
}
(map[string]interface {}) (len=1) {
(string) (len=4) "data": (map[string]interface {}) (len=2) {
(string) (len=7) "address": (string) (len=3) "::1",
(string) (len=7) "version": (float64) 4
}
Conclusion
The result is depends on JSONUnmarshaler. For now, you could set to more suitable unmarshaler (if you have).
However, I think your concerns could be input for other unmarshaler options. Both from internal and external packages.
Could we consider this ? @jeevatkm
@maticmeznar Thanks for reaching out.
I don't know if this expected result could be achieved the way it is. You could try to define a custom type for the outer map.
@kecci Thanks for responding to the comment. Could you explain what to consider? I'm unable to understand.