Unable to handle nil pointers when implementing Encoder
meblum opened this issue · comments
With the default decoding, a nil pointer is encoded as an empty string (unless omitempty was specified) so a nil *int will encode to an empty string. But when implementing a custom encoder, there is no way to check if value was nil as the value will be initialized to the zero value before EncodeValues is called.
Could you not just use a pointer to your custom type to allow for the same kind of nil checking?
For example, the following works as I would expect, and I think as you're describing:
package main
import (
"fmt"
"net/url"
"github.com/google/go-querystring/query"
)
type T int
func (t T) EncodeValues(key string, v *url.Values) error {
v.Add(key, fmt.Sprintf("_%d_", t))
return nil
}
type options struct {
*T `url:"t,omitempty"`
}
func main() {
v, _ := query.Values(options{})
fmt.Println(v.Encode()) // will output: ""
v, _ = query.Values(options{T: new(T)})
fmt.Println(v.Encode()) // will output: "t=_0_"
}
If that doesn't work for you, could you share a code snippet of what you're trying to do?
oh, the other thing you can do is implement the zeroable
interface by adding an IsZero() bool
method on your custom type. That way, you can do whatever kind of zero checking makes sense for that type. But again, I'm not sure if that covers your use case or not.
Sorry I wasn't clear enough. Was trying to do something like this https://play.golang.org/p/wnzO0oIJI5i
package main
import (
"fmt"
"net/url"
"github.com/google/go-querystring/query"
)
type T int
// EncodeValues encodes a 0 as false, 1 as true, and nil as unknown
func (t *T) EncodeValues(key string, v *url.Values) error {
if t == nil {
v.Add(key, "unknown")
return nil
}
if *t == 0 {
v.Add(key, "false")
return nil
}
if *t == 1 {
v.Add(key, "true")
return nil
}
return fmt.Errorf("value not supported")
}
type options struct {
*T `url:"t"`
}
func main() {
v, _ := query.Values(options{})
fmt.Println(v.Encode()) // will output: false, expected unknown
}
Got it, that makes sense.
That will definitely take a little bit of work to try and make it possible. I tried a naive approach, and keep running into places throughout the package that chokes on nil values because it assumes it gets handled earlier :)
Actually, I'm not even sure if this is possible at all. Since it's a nil pointer, I don't believe there's any way to know what type it would have pointed to is. I believe nil interfaces carry their type information along, but not nil pointers. I could be wrong on that, and will keep looking into it, just sort of thinking out loud here.
Yeah, scratch that (I think)
I'm actually trying to do the opposite to the examples you added into the tests. I'm trying to convert a bool false
to 0
and true
to 1
.
I'm having an issue with a non-pointer (e.g. T(false)
) not triggering EncodeValues()
to be called. Which means the false
is omitted from the encoded output as if it was never set, when it was.
Here's an example of what I'm trying:
https://goplay.tools/snippet/DMgWjMLCtu1
NOTE: In my example I could remove the
omitempty
from theTee
field's struct tag but then the first example where I don't set theTee
field would then be set to its zero value and I have no way to know if it was set to false or just omitted.
Not sure if you're able to offer any guidance on how best to achieve this. Thanks.