Structure fields original order messed up
achesco opened this issue · comments
Hi
Thanks for your work!
Code sample from README states:
type Options struct {
Query string `url:"q"`
ShowAll bool `url:"all"`
Page int `url:"page"`
}
opt := Options{ "foo", true, 2 }
v, _ := query.Values(opt)
fmt.Print(v.Encode()) // will output: "q=foo&all=true&page=2"
Question is about this: fmt.Print(v.Encode()) // will output: "q=foo&all=true&page=2"
But the actual output is all=true&page=2&q=foo
for me. It could be critical, for example, if you need to generate payload based signature, since json.Marshal(opt)
keeps original keys order:
type Options struct {
Query string `url:"q" json:"q"`
ShowAll bool `url:"all" json:"all"`
Page int `url:"page" json:"page"`
}
opt := Options{ "foo", true, 2 }
v, _ := json.Marshal(opt)
fmt.Print(string(js)) // outputs {"q":"foo","all":true,"page":2}
Thanks for the report. I suspect that this is because url.Values
is a map[string][]string
, whose key access is by definition unordered. There's not really much go-querystring can do about that. That's a good callout though, and we could certainly add a reminder in the docs somewhere that if stable ordering is needed, then the caller will need to handle that. I haven't looked, but I would imagine encoding/json
doesn't have this problem because it's using its own internal data structures rather than maps.
I'm curious though, is this causing problems in practice? You mention signatures, but I would assume that signatures would be generated on the output of encoding the url.Values
, so as long as you're generating the signature on the same encoding that is passed in a URL, then the ordering shouldn't matter. And I don't say that to dismiss the concern... only to better understand the severity.
I wouldn't insist on "severity" actually :) My practical case was about the API requires request body payload to be accompanied by a signature header calculated on the string represented by param1=value1¶m2=value2
pairs of actual JSON payload. Possible workaround in my case is to use a query string to pass all the params I need. Or to relay on map
for both payload forming and signature generation.
Another type of problem I can think about is when values of the params with the same names are overridden by the latter ones under some circumstances... not critical either.
Suppose we can close the issue then