yaml: unmarshal errors: cannot unmarshal string into time.Duration in Golang

Clash Royale CLAN TAG#URR8PPP
yaml: unmarshal errors: cannot unmarshal string into time.Duration in Golang
I have struct as below:
type Connect struct
ClientID string `yaml:"clientid"`
Password string `yaml:"password"`
Timeout time.Duration `yaml:"timeout"`
c1 := `
id: 'client1'
password: 'hhhhhhha'
timeout: 10
`
c2 := `
id: 'client2'
password: 'llllllla'
timeout: '10'
`
c3 := `
id: 'client3'
password: 'hhhhhhha'
timeout: 10s
`
c4 := `
id: 'client4'
password: 'llllllla'
timeout: '10s'
`
as shown above, type of Timeout is time.Duration, the default unit is Nanosecond, but I want to get the result: c1 && c2 has error, c3 && c4 is valid(the config of Timeout must have unit). How should I do to rewrite the UnmarshalYAML() Method for yaml? Thanks a lot.
2 Answers
2
I would create an aliased type in the UnmarshalYAML function so that all the values could be unmarshaled to some primitive types. Then I will rewrite those values that match and convert those which do not:
UnmarshalYAML
package main
import (
"fmt"
"time"
"gopkg.in/yaml.v2"
)
type Connect struct
ClientID string `yaml:"clientid"`
Password string `yaml:"password"`
Timeout time.Duration `yaml:"timeout"`
func (ut *Connect) UnmarshalYAML(unmarshal func(interface) error) error
type alias struct
ClientID string `yaml:"clientid"`
Password string `yaml:"password"`
Timeout string `yaml:"timeout"`
var tmp alias
if err := unmarshal(&tmp); err != nil
return err
t, err := time.ParseDuration(tmp.Timeout)
if err != nil
return fmt.Errorf("failed to parse '%s' to time.Duration: %v", tmp.Timeout, err)
ut.ClientID = tmp.ClientID
ut.Password = tmp.Password
ut.Timeout = t
return nil
func main()
c1 := `
id: 'client1'
password: 'hhhhhhha'
timeout: 10
`
c2 := `
id: 'client2'
password: 'llllllla'
timeout: '10'
`
c3 := `
id: 'client3'
password: 'hhhhhhha'
timeout: 10s
`
c4 := `
id: 'client4'
password: 'llllllla'
timeout: '10s'
`
cc := stringc1, c2, c3, c4
for i, cstr := range cc
var c Connect
err := yaml.Unmarshal(byte(cstr), &c)
if err != nil
fmt.Printf("Error for c%d: %vn", (i + 1), err)
continue
fmt.Printf("c%d: %+vn", (i + 1), c)
The output looks as follows:
$ go run main.go
Error for c1: failed to parse '10' to time.Duration: time: missing unit in duration10
Error for c2: failed to parse '10' to time.Duration: time: missing unit in duration10
c3: ClientID: Password:hhhhhhha Timeout:10s
c4: ClientID: Password:llllllla Timeout:10s
One way would be to make a custom type for Timeout that implements the Unmarshaler interface, if you're not able to do it in the UnmarshalYAML method of Connect:
Timeout
UnmarshalYAML
Connect
type Connect struct
ClientID string `yaml:"clientid"`
Password string `yaml:"password"`
Timeout UnmarshalingTimeout `yaml:"timeout"`
type UnmarshalingTimeout time.Duration
func (ut UnmarshalingTimeout) UnmarshalYAML(unmarshal func(interface) error) error
// implement unmarshaling here
Thanks you. But when I compiled, it has another error "cannot use Connect.Timeout (type UnmarshalingTimeout) as type time.Duration in field value "
– kuailehaibin
Aug 10 at 7:55
@kuailehaibin Unfortunately, you will need to convert back and forth to use as time.Duration, but it's easily done:
time.Duration(Connect.Timeout)– threeve
Aug 10 at 12:21
time.Duration(Connect.Timeout)
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Many thanks, it works for me.
– kuailehaibin
Aug 10 at 10:02