【问题标题】:yaml: unmarshal errors: cannot unmarshal string into time.Duration in Golangyaml:解组错误:无法将字符串解组为时间。Golang中的持续时间
【发布时间】:2018-08-09 19:33:02
【问题描述】:

我的结构如下:

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'
    `

如上图,Timeout的类型是time.Duration,默认单位是纳秒,但是我想得到结果:c1 && c2有错误,c3 && c4有效(Timeout的配置必须有单位)。我应该如何为 yaml 重写 UnmarshalYAML() 方法?非常感谢。

【问题讨论】:

    标签: go yaml unmarshalling


    【解决方案1】:

    我将在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 := []string{c1, 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: %v\n", (i + 1), err)
                continue
            }
            fmt.Printf("c%d: %+v\n", (i + 1), c)
        }
    }
    

    输出如下:

    $ 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}
    

    【讨论】:

      【解决方案2】:

      如果您无法在 ConnectUnmarshalYAML 方法中执行此操作,则一种方法是为实现 Unmarshaler 接口的 Timeout 创建自定义类型:

      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
      }
      

      【讨论】:

      • 谢谢。但是当我编译时,它有另一个错误“不能使用 Connect.Timeout (type UnmarshalingTimeout) as type time.Duration in field value”
      • @kuailehaibin 不幸的是,您需要来回转换以用作 time.Duration,但很容易做到:time.Duration(Connect.Timeout)
      【解决方案3】:

      time.Duration 的解组在 yaml.v3、https://play.golang.org/p/-6y0zq96gVz 中工作

      package main
      
      import (
          "fmt"
          "time"
          
          "gopkg.in/yaml.v3"
      )
      
      type Config struct {
          Timeout time.Duration `yaml:"timeout"`
      }
      
      func main() {
          var cfg Config
      
          b := []byte(`timeout: 60s`)
          yaml.Unmarshal(b, &cfg)
          fmt.Printf("timeout: %dm", int(cfg.Timeout.Minutes()))
      }
      

      【讨论】:

        【解决方案4】:

        目前它也适用于gopkg.in/yaml.v2,因此接受的答案已过时

        https://play.golang.org/p/lbragq1QNqD

        package main
        
        import (
            "fmt"
            "time"
        
            "gopkg.in/yaml.v2"
        
        )
        
        type Config struct {
            Timeout time.Duration `yaml:"timeout"`
        }
        
        
        func main() {
            sample := `
        timeout: 1s
        `
            var b Config
            if err := yaml.Unmarshal([]byte(sample), &b); err != nil {
                panic(err)
            }
            fmt.Printf("%+v", b)
        }
        
        

        【讨论】:

          【解决方案5】:
          type system struct {
              ...
              TokenLifeTime yamlTimeDur  `yaml:"TokenLifeTime"`
          }
          
          type yamlTimeDur time.Duration
          
          func (t *yamlTimeDur) UnmarshalYAML(unmarshal func(interface{}) error) error {
              var tm string
              if err := unmarshal(&tm); err != nil {
                  return err
              }
          
              td, err := time.ParseDuration(tm)
              if err != nil {
                  return fmt.Errorf("failed to parse '%s' to time.Duration: %v", tm, err)
              }
          
              *t = yamlTimeDur(td)
              return nil
          }
          
          func (t *yamlTimeDur) Duration() time.Duration {
              return time.Duration(*t)
          }
          

          【讨论】:

            猜你喜欢
            • 2022-11-14
            • 2012-06-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-02-28
            • 2021-02-11
            • 1970-01-01
            相关资源
            最近更新 更多