英文:
Create custom type that will seem like the another when checking types Golang
问题
我是一名经验丰富的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
类型,或者如何为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)
}
希望这可以帮助到你。如果你有任何其他问题,请随时问我。
英文:
I am a experienced python programmer but I am still new to Golang so my apologies if this is an obvious or silly question. But I am trying to create my own type that I want to act exactly like the base type with the exception of several methods being overridden. The reason for this is because several libraries I am using are checking the type against time.Time
and I want it to match.
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"`
}
So the the above works fine for marshalling and unmarshalling JSON. However, for my library that I am using (gocql and cqlr) they are checking if the type is a time.Time
type so they can make some other modifications before putting it in C*. How do I get my PythonTime
type to equate to either show as time.Time
or override the default marshalling/unmarshalling for a time.Time
object just for the use of my OtherType
objects?
My temporary solution has been to modify their code and add a special case for the PythonTime
type that does the same thing as the time.Time
type. However, this is causing me circular imports and is not the best solution. Here is their code with my modifications.
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)
}
答案1
得分: 2
不要这样做。你正在检查一个 time.Time
对象,而你应该检查它是否满足一个接口。
type TimeLike interface {
Day() int
Format(string) string
... // 任何使代码成为“time”对象的东西!
// 在这种情况下看起来是
UTC() time.Time
IsZero() bool
}
然后,任何期望一个可以用 PythonTime
替代的 time.Time
的代码,都应该期望一个 TimeLike
。
func Foo(value interface{}) int {
switch v := value.(type) {
case TimeLike:
return v.Day() // 对于 time.Time 或 models.PythonTime 都适用
}
return 0
}
英文:
Don't do this. You're checking for a time.Time
object when you should be checking that it satisfies an interface.
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
}
then any code that expects a time.Time
that can be substituted with a PythonTime
, expect a TimeLike
instead.
function Foo(value interface{}) int {
switch v := value.(type) {
case TimeLike:
return v.Day() // works for either time.Time or models.PythonTime
}
return 0
}
答案2
得分: 1
就像你在json.Marshaler
和json.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的上下文中未经测试,但这在自身中是往返的)
英文:
Just like you have done with the json.Marshaler
and json.Unamrshaler
, you can also implement the gocql.Marshaler
gocql.Unamrshaler
interfaces.
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
}
(note, untested in the context of CQL, but this does round-trip with itself)
答案3
得分: 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
英文:
Unfortunately, that will not work in Go. Your best option would be to create some import and export methods, so that you can cast your PythonTime to a time.Time and vice versa. That will give you flexibility you desire along with compatibility with other libraries.
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
}
That should give output like this:
2016-02-04 14:32:17-0700 *main.PythonTime
2016-02-04 14:32:17 -0700 MST time.Time
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论