UnmarshalText in Go
I must admit that I tend to overlook the interfaces in the
encoding package. Since I typically
work with JSON or Yaml, so I tend to care more about methods like
UnmarshalJSON. However, today I learned the trick of using TextUnmarshaler
when I needed to use a custom type inside the struct.
Problem
Consider this code
package main
import (
"encoding/json"
"fmt"
"net/url"
)
type Foo struct {
BaseURL *url.URL `json:"base_url"`
}
func main() {
const src = `{"base_url": "https://example.com"}`
var aFoo Foo
err := json.Unmarshal([]byte(src), &aFoo)
if err != nil {
panic(err)
}
fmt.Printf("%#+v\n", aFoo.BaseURL.String())
}The standard *url.URL can’t do that. There’s
UnmarshalBinary, but no other
method available.
panic: json: cannot unmarshal string into Go struct field Foo.base_url of type url.URL
UnmarshalText
json.Unmarshal documents how it works
To unmarshal JSON into a value implementing [Unmarshaler], Unmarshal calls that value’s [Unmarshaler.UnmarshalJSON] method, including when the input is a JSON null. Otherwise, if the value implements [encoding.TextUnmarshaler] and the input is a JSON quoted string, Unmarshal calls [encoding.TextUnmarshaler.UnmarshalText] with the unquoted form of the string.
The only thing needed to be implemented is TextUnmarshaler interface. Because URL is
a string in JSON.
type URL struct {
*url.URL
}
func (u *URL) UnmarshalText(text []byte) error {
parsed, err := url.Parse(string(text))
if err != nil {
return err
}
u.URL = parsed
return nil
}And the output is "https://example.com".
Marshal it back
When marshaled back to JSON, the URL looks like this: No one wants to write URLs like this.
{
"u": {
"Scheme": "https",
"Opaque": "",
"User": null,
"Host": "example.com",
"Path": "",
"RawPath": "",
"OmitHost": false,
"ForceQuery": false,
"RawQuery": "",
"Fragment": "",
"RawFragment": ""
}
}Obviously there is an interface for that
func (u URL) MarshalText() ([]byte, error) {
if u.URL == nil {
return []byte{}, nil
}
return []byte(u.URL.String()), nil
}{"base_url":"https://example.com"}Conclusion
While the text encoding methods are somewhat less popular than their
encoding/json counterparts, they can be still useful.
See complete example on Go playground.
Logo is remixed github.com/egonelbre/gophers and is under CC0 license like an original.