为了账号安全,请及时绑定邮箱和手机立即绑定

如何在 Unmarshal 中使用泛型 (go 1.18)

如何在 Unmarshal 中使用泛型 (go 1.18)

Go
喵喔喔 2022-12-05 16:38:05
我是 golang 泛型的新手,具有以下设置。我收集了大量不同类型的报告。每个报告都有封闭的字段所以我把它包裹在一个ReportContainerImpl我使用了一个类型参数, [T Reportable]其中Reportable定义如下type Reportable interface {    ExportDataPointReport | ImportDataPointReport | MissingDataPointReport | SensorThresoldReport}类型约束中的每个类型都是要嵌入到容器中的结构。type ReportContainerImpl[T Reportable] struct {    LocationID string `json:"lid"`    Provider string `json:"pn"`    ReportType ReportType `json:"m"`    Body T `json:"body"`}我使用鉴别ReportType器来确定具体类型 when Unmarshal。type ReportType stringconst (    ReportTypeExportDataPointReport ReportType = "ExportDataPointReport"    ReportTypeImportDataPointReport ReportType = "ImportDataPointReport"    ReportTypeMissingDataPointReport ReportType = "MissingDataPointReport"    ReportTypeSensorThresoldReport ReportType = "SensorThresoldReport")由于go不支持struct的类型断言(仅接口),因此无法在Unmarshal. go也不支持指向“原始”泛型类型的指针。因此,我创建了一个实现的接口ReportContainerImpl。type ReportContainer interface {    GetLocationID() string    GetProvider() string    GetReportType() ReportType    GetBody() interface{}}然后我得到的问题是我不能以任何形式或形状对返回类型进行类型约束,并且回到函数的“自由文本语义”以GetBody()允许在完成时进行类型断言Unmarshal。    container, err := UnmarshalReportContainer(data)    if rep, ok := container.GetBody().(ExportDataPointReport); ok {      // Use the ReportContainerImpl[ExportDataPointReport] here...    }也许我弄错了?- 但无论我这样做,我总是在某个地方结束之前需要一个interface{}或知道确切的类型Unmarshal你有更好的建议如何以一种类型(更安全)的方式解决这个问题吗?干杯,马里奥 :)为了完整起见,我在UnmarshalReportContainer这里添加func UnmarshalReportContainer(data []byte) (ReportContainer, error) {    type Temp struct {        LocationID string `json:"lid"`        Provider string `json:"pn"`        ReportType ReportType `json:"m"`        Body *json.RawMessage `json:"body"`    }    var temp Temp    err := json.Unmarshal(data, &temp)    if err != nil {        return nil, err    }
查看完整描述

1 回答

?
凤凰求蛊

TA贡献1825条经验 获得超4个赞

但是无论我这样做,我总是在某处需要一个接口{}或在 Unmarshal 之前知道确切的类型


恰恰。


在编写代码时,实例化某些泛型类型或函数所需的具体类型ReportContainerImpl必须UnmarshalReportContainer在编译时知道。当您用实际数据填充字节片时,JSON 解组发生在运行时。


要根据某些区分值解组动态 JSON,您仍然需要一个switch.


你有更好的建议如何以一种类型(更安全)的方式解决这个问题吗?


只是放弃参数多态性。这里不太合适。保留您现在拥有的代码json.RawMessage,在 中有条件地解组动态数据,switch并返回实现ReportContainer接口的具体结构。


作为通用解决方案——当且仅当您可以克服这个先有鸡还是先有蛋的问题并在编译时使类型参数已知时,您可以编写一个最小的通用解组函数,如下所示:


func unmarshalAny[T any](bytes []byte) (*T, error) {

    out := new(T)

    if err := json.Unmarshal(bytes, out); err != nil {

        return nil, err

    }

    return out, nil

}

这只是为了说明原理。请注意,它json.Unmarshal已经接受任何类型,因此如果您的泛型函数实际上除了返回并没有执行任何操作new(T),就像在我的示例中一样,它与“内联”整个事物没有什么不同,就好像它unmarshalAny不存在一样。


v, err := unmarshalAny[SomeType](src)

功能上等同于


out := &SomeType{}

err := json.Unmarshal(bytes, out)

如果您打算在 中放入更多逻辑unmarshalAny,则可能需要使用它。你的旅费可能会改变; 通常,在实际上不需要时不要使用类型参数。


查看完整回答
反对 回复 2022-12-05
  • 1 回答
  • 0 关注
  • 479 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号