如何在 Unmarshal 中使用泛型 (go 1.18)
How to use generics in Unmarshal (go 1.18)
我是 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"`
}
当Unmarshal
时,我使用鉴别器ReportType
来确定具体类型。
type ReportType string
const (
ReportTypeExportDataPointReport ReportType = "ExportDataPointReport"
ReportTypeImportDataPointReport ReportType = "ImportDataPointReport"
ReportTypeMissingDataPointReport ReportType = "MissingDataPointReport"
ReportTypeSensorThresoldReport ReportType = "SensorThresoldReport"
)
因为 go 不支持 struct 的类型断言(仅 interfaces)它是Unmarshal
时无法转换类型。此外 go 不支持指向 "raw" 通用类型的指针。因此,我创建了 接口 ReportContainerImpl
实现。
type ReportContainer interface {
GetLocationID() string
GetProvider() string
GetReportType() ReportType
GetBody() interface{}
}
然后我遇到的问题是我无法以任何形式或形状对 return 类型进行类型约束,我回到 “自由文本语义” GetBody()
函数允许在 Unmarshal
完成时进行类型断言。
container, err := UnmarshalReportContainer(data)
if rep, ok := container.GetBody().(ExportDataPointReport); ok {
// Use the ReportContainerImpl[ExportDataPointReport] here...
}
也许我理解错了? - 但是无论我这样做,我总是在某处需要 interface{}
或在 Unmarshal
之前知道 exact 类型
- 对于如何以类型(更安全)的方式解决此问题,您有更好的建议吗?
干杯,
马里奥 :)
为了完整起见,我在此处添加 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
// ...
}
}
but however I do this, I always end up with somewhere needs a interface{} or to know the exact type before Unmarshal
没错。
实例化某些泛型类型或函数(如 ReportContainerImpl
或 UnmarshalReportContainer
所需的具体类型必须在编写代码时的编译时已知。 JSON 解组发生在 run-time,当你用实际数据填充字节片时。
要根据某些区分值解组动态 JSON,您仍然需要 switch
。
Do you have a better suggestion how to solve this in a type (safer) way?
只是放弃参数多态性。这里不太合适。保留您现在使用 json.RawMessage
的代码,在 switch
和 return 实现 ReportContainer
接口的具体结构中有条件地解组动态数据。
附带说明一下,如果您发现自己使用多种结构类型编写联合,这也表明您可能需要使用普通的旧接口而不是类型参数。
我是 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"`
}
当Unmarshal
时,我使用鉴别器ReportType
来确定具体类型。
type ReportType string
const (
ReportTypeExportDataPointReport ReportType = "ExportDataPointReport"
ReportTypeImportDataPointReport ReportType = "ImportDataPointReport"
ReportTypeMissingDataPointReport ReportType = "MissingDataPointReport"
ReportTypeSensorThresoldReport ReportType = "SensorThresoldReport"
)
因为 go 不支持 struct 的类型断言(仅 interfaces)它是Unmarshal
时无法转换类型。此外 go 不支持指向 "raw" 通用类型的指针。因此,我创建了 接口 ReportContainerImpl
实现。
type ReportContainer interface {
GetLocationID() string
GetProvider() string
GetReportType() ReportType
GetBody() interface{}
}
然后我遇到的问题是我无法以任何形式或形状对 return 类型进行类型约束,我回到 “自由文本语义” 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
}
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
// ...
}
}
but however I do this, I always end up with somewhere needs a interface{} or to know the exact type before Unmarshal
没错。
实例化某些泛型类型或函数(如 ReportContainerImpl
或 UnmarshalReportContainer
所需的具体类型必须在编写代码时的编译时已知。 JSON 解组发生在 run-time,当你用实际数据填充字节片时。
要根据某些区分值解组动态 JSON,您仍然需要 switch
。
Do you have a better suggestion how to solve this in a type (safer) way?
只是放弃参数多态性。这里不太合适。保留您现在使用 json.RawMessage
的代码,在 switch
和 return 实现 ReportContainer
接口的具体结构中有条件地解组动态数据。
附带说明一下,如果您发现自己使用多种结构类型编写联合,这也表明您可能需要使用普通的旧接口而不是类型参数。