我有一个 API,它通常将数组作为包含数组的对象返回。以下面的例子为例:
<code>{ "items": { "number": 3, "item": [ { ... } // Not relevant ] } } </code>
API 在数十个地方执行此操作,每次都使用不同的名称。保证发生这种情况时只有两个键:其中一个是 number
,另一个是数组。
这使得生成的结构使用起来相当不愉快,因为您必须不断地浏览不必要的字段级别。
我本质上希望我的 Go 界面假装它具有这种格式:
<code>{ "items": [ { ... } // Not relevant ] } </code>
一种选择是为每次出现的情况编写一个自定义 UnmarshalJSON
函数,但这似乎很麻烦,特别是考虑到它几乎出现在每个结构中。我想到的解决方案是一个可以自行处理它的泛型类型。
我当前的尝试如下:
<code>// 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 } </code>
使用方法如下:
<code>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"` } </code>
导致以下错误:
json: cannot unmarshal array into Go struct field Root.items of type map[string]interface{}
UnmarshalJSON
代码的最大部分似乎是正确的,因为我的调试器向我显示 sliceVal
正是我所期望的。解组回 NestedArray[T]
代码的最大部分似乎是正确的,因为我的调试器向我显示 sliceVal
正是我所期望的。解组回 NestedArray[T]
类型时出错。
有什么办法可以解决这个问题?有比我现在正在做的更好的方法吗?这对我来说似乎是最干净的,但我愿意接受建议。
方法 NestedArray[T].UnmarshalJSON 递归地调用自身。内部调用会引发错误,因为它需要 bytes
中的 JSON 对象,但它收到了一个 JSON 数组。通过解组到 []T
而不是 NestedArray[T]
来修复。
与错误无关,方法 NestedArray[T].UnmarshalJSON 执行了一些不必要的编码和解码。使用 json.RawMessage 进行修复。
这是包含两个修复的代码:
func (n *NestedArray[T]) UnmarshalJSON(bytes []byte) error { // First unmarshal into a map var target map[string]json.RawMessage 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 array json.RawMessage for k, v := range target { if k == "number" { continue } if len(v) > 0 && v[0] == '[' { array = v break } } // Missing or empty, doesn't matter - set the result to nil if array == nil { *n = nil return nil } // Avoid recursive call to this method by unmarshalling to a []T. var v []T err = json.Unmarshal(array, &v) *n = v return err }
以上是将结构解组为切片的通用类型定义的详细内容。更多信息请关注PHP中文网其他相关文章!