猿问

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

我是 golang 泛型的新手,具有以下设置。

  1. 我收集了大量不同类型的报告。

  2. 每个报告都有封闭的字段

  3. 所以我把它包裹在一个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 string


const (

    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

    }



喵喔喔
浏览 480回答 1
1回答

凤凰求蛊

但是无论我这样做,我总是在某处需要一个接口{}或在 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,则可能需要使用它。你的旅费可能会改变; 通常,在实际上不需要时不要使用类型参数。
随时随地看视频慕课网APP

相关分类

Go
我要回答