google / go-querystring

go-querystring is Go library for encoding structs into URL query strings.

Home Page:https://pkg.go.dev/github.com/google/go-querystring/query

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Custom `String` implementations are not respected

zakcutner opened this issue · comments

Hello! I expect the following example to print int=23&foo=bar, but instead it prints int=23. It looks like this was previously working in #45, so there might have been a regression.

package main

import (
	"fmt"

	"github.com/google/go-querystring/query"
)

type Foo struct{}

func (f Foo) String() string {
	return "bar"
}

type Options struct {
	Int int `url:"int"`
	Foo Foo `url:"foo"`
}

func main() {
	opt := Options{23, Foo{}}
	v, _ := query.Values(opt)
	fmt.Println(v.Encode())
}

because type Foo is a struct, the default behavior is for go-querystring to try and process the fields of that struct. In this case, it has no fields, so there's nothing to encode. If you want custom behavior, you would need to implement the query.Encoder interface.

The reason a value was being returned in #45 (I think) is because the field in question was an int, which defaults to its string representation.

This seems to do what you want: https://go.dev/play/p/K4rYQ7Oeq0k

Thanks for answering so quickly! I don't completely agree about #45 since they said

// got "level=info", want "level=1"

If it was using a default string representation, wouldn't level=1 be returned? In any case, the docs say that

All other values are encoded using their default string representation.

Sorry if I'm missing something but it seems to me that this behaviour conflicts with that.

ah, you're right about it not being the default string representation. But the difference in behavior is still due to using a struct versus non-struct field. The relevant code is here.

In your case, sv.Kind() of your Foo field does return a struct, so it is processed as a struct. In the case of #45, sv.Kind() is an int, so it gets processed by the valueString() func. Which as you point out, uses the String() method of the type if present, hence the "level=info".

The existing docs do talk about struct fields a little bit, but if there's a way we can describe this behavior better to make it more obvious, I'd welcome that.

Maybe it could be as simple as changing this line in the docs:

Nested structs are encoded including parent fields in value names for scoping.

to something like:

Nested structs have their fields processed recursively and are encoded including parent fields in value names for scoping.

Would that be clearer?

(And here's a quick example I did to double check myself that sv.Kind() returns the underlying kind, even for custom types... https://go.dev/play/p/MJUTVO5RZWn)

Thanks so much, yes I think that would be clearer 😄