我是新来的金刚泛型,并有以下设置。
中
我使用了[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来确定Unmarshal时的具体类型。
type ReportType string
const (
ReportTypeExportDataPointReport ReportType = "ExportDataPointReport"
ReportTypeImportDataPointReport ReportType = "ImportDataPointReport"
ReportTypeMissingDataPointReport ReportType = "MissingDataPointReport"
ReportTypeSensorThresoldReport ReportType = "SensorThresoldReport"
)由于go不支持struct的类型断言(仅支持接口),所以当Unmarshal时不可能转换该类型。此外,go不支持指向"raw“泛型的指针。因此,我创建了一个ReportContainerImpl实现的接口。
type ReportContainer interface {
GetLocationID() string
GetProvider() string
GetReportType() ReportType
GetBody() interface{}
}然后我遇到的问题是,我不能以任何形式或形状对返回类型进行类型约束,并且返回到GetBody()函数的"freetext语义“,以便在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
}
switch temp.ReportType {
case ReportTypeExportDataPointReport:
var report ExportDataPointReport
err := json.Unmarshal(*temp.Body, &report)
return &ReportContainerImpl[ExportDataPointReport]{
LocationID: temp.LocationID,
Provider: temp.Provider,
ReportType: temp.ReportType,
Body: report,
}, err
// ...
}
}发布于 2022-04-21 13:35:57
--但是不管我怎么做,我最终总是需要一个接口{},或者在解组之前知道确切的类型
确切地说。
在编写代码时,必须在编译时知道实例化某些泛型类型或函数(如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中添加更多的逻辑,则可能需要使用它。您的里程可能会有所不同;一般来说,在没有必要的情况下,不要使用类型参数。
https://stackoverflow.com/questions/71955113
复制相似问题