如何在Unmarshal中使用仿制药(GO 1.18)
我是Golang Generics的新手,并拥有以下设置。
- 我收集了许多不同类型的报告。
- 每个报告都包含字段
- ,因此我将其包装在
reportContainerimpl
我使用的类型参数[t efortable] 中,其中定义了efortboble
如下所示,
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 (仅 interfaces )的类型断言,因此当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...
}
也许我弄错了? - 但是我这样做,我总是最终到达某个地方需要一个接口{}
或在 exact eact type之前unmarshal
- 您是否有更好的建议如何以类型(更安全的)方式解决这个问题?
干杯, Mario :)
为了完整性,我在此处添加umarshalreportContainer
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
// ...
}
}
I'm new to golang generics and have the following setup.
- I've gathered loads of different kinds of reports.
- Each report has enclosing fields
- So I wrapped it in a
ReportContainerImpl
I've used a type argument of [T Reportable]
where the Reportable
is defined as follows
type Reportable interface {
ExportDataPointReport | ImportDataPointReport | MissingDataPointReport | SensorThresoldReport
}
Each of the type in the type constraint is structs that is to be embedded in the container.
type ReportContainerImpl[T Reportable] struct {
LocationID string `json:"lid"`
Provider string `json:"pn"`
ReportType ReportType `json:"m"`
Body T `json:"body"`
}
I use a discriminator ReportType
to determine the concrete type when Unmarshal
.
type ReportType string
const (
ReportTypeExportDataPointReport ReportType = "ExportDataPointReport"
ReportTypeImportDataPointReport ReportType = "ImportDataPointReport"
ReportTypeMissingDataPointReport ReportType = "MissingDataPointReport"
ReportTypeSensorThresoldReport ReportType = "SensorThresoldReport"
)
Since go does not support type assertion for struct (only interfaces) it is not possible to cast the type when Unmarshal
. Also go does not support pointer to the "raw" generic type. Hence, I've created a interface that the ReportContainerImpl
implements.
type ReportContainer interface {
GetLocationID() string
GetProvider() string
GetReportType() ReportType
GetBody() interface{}
}
The problem I then get is that I cannot do type constrains on the return type in any form or shape and am back at "freetext semantics" on the GetBody()
function to allow for type assertion when Unmarshal
is done.
container, err := UnmarshalReportContainer(data)
if rep, ok := container.GetBody().(ExportDataPointReport); ok {
// Use the ReportContainerImpl[ExportDataPointReport] here...
}
Maybe I'm getting this wrong? - but however I do this, I always end up with somewhere needs a interface{}
or to know the exact type before Unmarshal
- Do you have a better suggestion how to solve this in a type (safer) way?
Cheers,
Mario :)
For completeness I add the UnmarshalReportContainer
here
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
// ...
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
精确地知道unmarshal之前的确切类型。
实例化某些通用类型或功能所需的具体类型,例如
ReportContainerimpl
或unmarshalreportContainer
必须在编译时(编写代码)在编译时知道。相反,当您将字节切片带有实际数据时,JSON UNMARSHALLING会发生在运行时。要基于某些歧视价值来解开“动态JSON”,您仍然需要
switch
。仅仅是放弃参数多态性。这里不合适。保留您现在使用
json.rawmessage
的代码,在switch
中有条件地删除动态数据,然后返回实现report> Report> ReportContainer
接口的混凝土结构。作为一般解决方案 - 如果并且只有在您可以克服这个鸡和蛋的问题并在编译时间已知的类型参数时,您可以写出这样的最小通用的透明函数:
这仅是为了说明原理。请注意,
json.unmarshal
已经接受任何类型,因此,如果您的通用函数实际上没有执行任何操作“在unmarshalany
不存在”整个过程中“插入”。在功能上等效,就好像
您打算在
umarshalany
中放置更多逻辑一样,可能需要使用其用法。您的里程可能会有所不同;通常,在实际上不是必需的情况下,请勿使用类型参数。Precisely.
The concrete types needed to instantiate some generic type or function like
ReportContainerImpl
orUnmarshalReportContainer
must be known at compile time, when you write the code. JSON unmarshalling instead occurs at run-time, when you have the byte slice populated with the actual data.To unmarshal dynamic JSON based on some discriminatory value, you still need a
switch
.Just forgo parametric polymorphism. It's not a good fit here. Keep the code you have now with
json.RawMessage
, unmarshal the dynamic data conditionally in theswitch
and return the concrete structs that implementReportContainer
interface.As a general solution — if, and only if, you can overcome this chicken-and-egg problem and make type parameters known at compile time, you can write a minimal generic unmarshal function like this:
This is only meant to illustrate the principle. Note that
json.Unmarshal
already accepts any type, so if your generic function actually does nothing exceptnew(T)
and return, like in my example, it is no different than "inlining" the entire thing as ifunmarshalAny
didn't exist.functionally equivalent as
If you plan to put more logic in
unmarshalAny
, its usage may be warranted. Your mileage may vary; in general, don't use type parameters when it's not actually necessary.