gpt4 book ai didi

json - 将 JSON 动态解码为仅包含基于类型字段的顶级字段的结构

转载 作者:行者123 更新时间:2023-12-01 22:23:59 25 4
gpt4 key购买 nike

假设我有以下无法修改的事件结构(甚至是标签):

type openAudioStream struct {
Type string `json:"type"`
ID string `json:"id"`
Address string `json:"address"`
Channels int `json:"channels"`
}

type openVideoStream struct {
Type string `json:"type"`
ID string `json:"id"`
Address string `json:"address"`
AspectRatio string `json:"aspectRatio"`
}

我有一个 API 端点,它发出 JSON 字符串(我也无法修改),其中包含映射到这两个结构之一的事件,我无法提前知道它是哪一个,所以我需要以某种方式提取类型字段找出要实例化的结构,然后将 JSON 的其余部分解码到实例化的事件对象中。

我想到的第一个方法是调用 json.Unmarshal两次像这样:

func jsonUnmarshal(payload []byte) (Event, error) {
eventType := struct {
Type string
}{}

err := json.Unmarshal(payload, &eventType)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
}

var event Event
switch eventType.Type {
case "audio":
event = &openAudioStream{}
err = json.Unmarshal(payload, event)
case "video":
event = &openVideoStream{}
err = json.Unmarshal(payload, event)
default:
err = fmt.Errorf("unrecognised event type: %s", eventType.Type)
}

if err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
}

return event, nil
}

虽然这很好用,但遍历 JSON 字符串两次效率低下,所以我认为,也许我可以创建一个联合类型并使用它来解码 JSON,如下所示:

func notWorking(payload []byte) (Event, error) {
eventUnion := struct {
Type string `json:"type"`
openAudioStream
openVideoStream
}{}

err := json.Unmarshal(payload, &eventUnion)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
}

var event Event
switch eventUnion.Type {
case "audio":
event = &extractor.openAudioStream
case "video":
event = &extractor.openVideoStream
default:
return nil, fmt.Errorf("unrecognised event type: %s", eventUnion.Type)
}

return event, nil
}

除了是一个丑陋的黑客之外,如果嵌入式结构包含冲突的字段,这种方法就不起作用。 json 解码器只是 ignores them不产生任何错误。

最后我想起了 mapstructure可能会有所帮助,实际上,我可以像这样使用它:

func mapstructureDecode(payload []byte) (Event, error) {
var unmarshaledPayload map[string]interface{}

err := json.Unmarshal(payload, &unmarshaledPayload)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
}

var ok bool
var val interface{}
var eventType string
if val, ok = unmarshaledPayload["type"]; ok {
eventType, ok = val.(string)
}
if !ok {
return nil, fmt.Errorf("failed to determine event type: %v", err)
}

var event Event
switch eventType {
case "audio":
event = &openAudioStream{}
err = mapstructure.Decode(unmarshaledPayload, &event)
case "video":
event = &openVideoStream{}
err = mapstructure.Decode(unmarshaledPayload, event)
default:
err = fmt.Errorf("unrecognised event type: %s", eventType)
}

if err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
}

return event, nil
}

然而,使用这个库对于这个任务来说似乎有点矫枉过正,它需要添加 mapstructure如果输入 JSON 不遵循标准命名约定,则为结构字段添加标签,如果我有,这是一个问题,例如 aspect_ratio而不是 aspectRatio .

上述实验的完整代码可以在这里找到: https://play.golang.org/p/qTGoV6i8m5P

我很好奇是否有任何其他方法可以使用现有库来解决这个问题。我在想也许是对 json.RawMessage 的一些创造性使用和自定义 UnmarshalJSON方法可能会有所帮助,但如果我在事件结构中只有顶级字段,这似乎没有用。

最佳答案

您可以在组合结构中解码一次,然后根据类型创建新结构。


func jsonUnmarshal(payload []byte) (Event, error) {
data := struct {
Type string
ID string
Address string
Channels int
AspectRatio string
}{}
// Unmarshal in combined stuct
err := json.Unmarshal(payload, &data)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
}
var event Event
switch data.Type {
case "audio":
// Creating new stuct
event = &openAudioStream{Type: data.Type, ID: data.ID, Address: data.Address, Channels: data.Channels}
case "video":
event = &openVideoStream{Type: data.Type, ID: data.ID, Address: data.Address, AspectRatio: data.AspectRatio}
default:
err = fmt.Errorf("unrecognised event type: %s", data.Type)
}
if err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
}
return event, nil
}

关于json - 将 JSON 动态解码为仅包含基于类型字段的顶级字段的结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61165306/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com