在检查Golang

时间:2016-02-04 20:50:28

标签: go types

我是一名经验丰富的python程序员,但我还是Golang的新手,所以我很抱歉这是一个明显或愚蠢的问题。但我正在尝试创建自己的类型,我希望它的行为与基类型完全相同,但有几种方法被覆盖。原因是我正在使用的几个库正在检查time.Time的类型,我希望它匹配。

type PythonTime struct {
    time.Time
}

var pythonTimeFormatStr = "2006-01-02 15:04:05-0700"

func (self *PythonTime) UnmarshalJSON(b []byte) (err error) {
    // removes prepending/trailing " in the string
    if b[0] == '"' && b[len(b)-1] == '"' {
        b = b[1 : len(b)-1]
    }
    self.Time, err = time.Parse(pythonTimeFormatStr, string(b))
    return
}

func (self *PythonTime) MarshalJSON() ([]byte, error) {
    return []byte(self.Time.Format(pythonTimeFormatStr)), nil
}

type OtherType struct {
    Uuid     string     `json:"uuid`
    Second   PythonTime `json:"second"`
    Location string     `json:"location"`
    Action   string     `json:"action"`
    Duration int        `json:"duration"`
    Value    string     `json:"value"`
}

因此上述工作适用于编组和解组JSON。但是,对于我正在使用的库(gocql和cqlr),他们正在检查类型是否为time.Time类型,以便在将它放入C *之前可以进行一些其他修改。如何使我的PythonTime类型等同于显示为time.Time或覆盖time.Time对象的默认编组/解组仅仅是为了使用我的OtherType对象?

我的临时解决方案是修改他们的代码并为PythonTime类型添加一个与time.Time类型相同的特例。但是,这导致我进口循环,并不是最好的解决方案。以下是我修改后的代码。

func marshalTimestamp(info TypeInfo, value interface{}) ([]byte, error) {
    switch v := value.(type) {
    case Marshaler:
        return v.MarshalCQL(info)
    case int64:
        return encBigInt(v), nil
    case time.Time:
        if v.IsZero() {
            return []byte{}, nil
        }
        x := int64(v.UTC().Unix()*1e3) + int64(v.UTC().Nanosecond()/1e6)
        return encBigInt(x), nil
    case models.PythonTime:
        x := int64(v.UTC().Unix()*1e3) + int64(v.UTC().Nanosecond()/1e6)
        return encBigInt(x), nil
    }

    if value == nil {
        return nil, nil
    }

    rv := reflect.ValueOf(value)
    switch rv.Type().Kind() {
    case reflect.Int64:
        return encBigInt(rv.Int()), nil
    }
    return nil, marshalErrorf("can not marshal %T into %s", value, info)
}

3 个答案:

答案 0 :(得分:2)

不要这样做。当您检查它是否满足接口时,您正在检查time.Time对象。

type TimeLike interface {
    Day() int
    Format(string) string
    ...  // whatever makes a "time" object to your code!
    // looks like in this case it's
    UTC() time.Time
    IsZero() bool
}

然后任何期望time.Time可以用PythonTime替换的代码,而不是TimeLike

function Foo(value interface{}) int {
    switch v := value.(type) {
    case TimeLike:
        return v.Day()  // works for either time.Time or models.PythonTime
    }
    return 0
}

答案 1 :(得分:1)

就像您使用json.Marshalerjson.Unamrshaler一样,您也可以实施gocql.Marshaler gocql.Unamrshaler接口。

func (t *PythonTime) MarshalCQL(info gocql.TypeInfo) ([]byte, error) {
    b := make([]byte, 8)
    x := t.UnixNano() / int64(time.Millisecond)
    binary.BigEndian.PutUint64(b, uint64(x))
    return b, nil
}

func (t *PythonTime) UnmarshalCQL(info gocql.TypeInfo, data []byte) error {
    x := int64(binary.BigEndian.Uint64(data)) * int64(time.Millisecond)
    t.Time = time.Unix(0, x)
    return nil
}

(注意,在CQL的上下文中未经测试,但这会自行进行往返)

答案 2 :(得分:0)

不幸的是,这在Go中不起作用。您最好的选择是创建一些导入和导出方法,以便您可以将PythonTime转换为time.Time,反之亦然。这将为您提供所需的灵活性以及与其他库的兼容性。

package main

import (

    "fmt"
    "reflect"
    "time"

)

func main() {

    p, e := NewFromTime(time.Now())
    if e != nil {
        panic(e)
    }

    v, e := p.MarshalJSON()
    if e != nil {
         panic(e)
    }

    fmt.Println(string(v), reflect.TypeOf(p))

    t, e := p.GetTime()
    if e != nil {
        panic(e)
    }

    fmt.Println(t.String(), reflect.TypeOf(t))

}

type PythonTime struct {
    time.Time
}

var pythonTimeFormatStr = "2006-01-02 15:04:05-0700"

func NewFromTime(t time.Time) (*PythonTime, error) {

b, e := t.GobEncode()
if e != nil {
    return nil, e
}

p := new(PythonTime)
e = p.GobDecode(b)
if e != nil {
    return nil, e
}

    return p, nil
}

func (self *PythonTime) GetTime() (time.Time, error) {
    return time.Parse(pythonTimeFormatStr, self.Format(pythonTimeFormatStr))
}

func (self *PythonTime) UnmarshalJSON(b []byte) (err error) {
    // removes prepending/trailing " in the string
    if b[0] == '"' && b[len(b)-1] == '"' {
        b = b[1 : len(b)-1]
    }
    self.Time, err = time.Parse(pythonTimeFormatStr, string(b))
    return
}

func (self *PythonTime) MarshalJSON() ([]byte, error) {
    return []byte(self.Time.Format(pythonTimeFormatStr)), nil
}

那应该给出这样的输出:

2016-02-04 14:32:17-0700 *main.PythonTime

2016-02-04 14:32:17 -0700 MST time.Time