将结构解组为切片的通用类型别名

问题描述 投票:0回答:1

我有一个 API,通常将数组作为包含数组的对象返回。就拿下面的例子来说吧:

{
  "items": {
    "number": 3,
    "item": [
      { ... } // Not relevant
    ]
  }
}

API 在数十个地方执行此操作,每次都使用不同的名称。保证发生这种情况时只有两个键:其中一个是

number
,另一个是数组。

这使得生成的结构使用起来相当不愉快,因为您必须不断地浏览不必要的字段级别。

我本质上希望我的 Go 界面假装它具有这种格式:

{
  "items": [
    { ... } // Not relevant
  ]
}

一个选择是为每次出现的情况编写一个自定义

UnmarshalJSON
函数,但这似乎很麻烦,特别是考虑到它几乎出现在每个结构中。我想到的解决方案是一个可以自行处理它的泛型类型。

我当前的尝试如下:

// NestedArray tries to pull an unnecessarily nested array upwards
type NestedArray[T any] []T

func (n *NestedArray[T]) UnmarshalJSON(bytes []byte) error {
    // First unmarshal into a map
    target := make(map[string]interface{})

    err := json.Unmarshal(bytes, &target)
    if err != nil {
        return err
    }

    // Then find the nested array (key is unknown, so go off of the type instead)
    var sliceVal interface{}
    for k, v := range target {
        if k == "number" {
            continue
        }

        rt := reflect.TypeOf(v)
        if rt.Kind() == reflect.Slice {
            sliceVal = v
            break
        }
    }

    // Missing or empty, doesn't matter - set the result to nil
    if sliceVal == nil {
        *n = nil
        return nil
    }

    // Turn back into JSON and parse into correct target
    sliceJSON, err := json.Marshal(sliceVal)
    if err != nil {
        return err
    }

    err = json.Unmarshal(sliceJSON, n)  // Error occurs here
    if err != nil {
        return err
    }

    return nil
}

使用方法如下:

type Item struct {
  // Not relevant
}

type Root struct {
    // Use generic type to parse a JSON object into its nested array
    Items NestedArray[Item] `json:"items,omitempty"`
}

导致以下错误:

json: cannot unmarshal array into Go struct field Root.items of type map[string]interface{}

UnmarshalJSON
代码的最大部分似乎是正确的,因为我的调试器向我显示
sliceVal
正是我所期望的。当解组回
NestedArray[T]
类型时会出错。

这个问题的解决方案是什么?有比我现在正在做的更好的方法吗?这对我来说似乎是最干净的,但我愿意接受建议。

go unmarshalling
1个回答
0
投票

解组到与项目匹配的结构。将项目字段分配给接收者。

func (n *NestedArray[T]) UnmarshalJSON(bytes []byte) error {
    var target struct {
        Item []T
    }
    err := json.Unmarshal(bytes, &target)
    if err != nil {
        return err
    }
    *n = target.Item
    return nil
}

https://go.dev/play/p/FABXzetkZdH

© www.soinside.com 2019 - 2024. All rights reserved.