这个问题已经有答案了:
How to use generics in Unmarshal (go 1.18)(1个答案)
5天前关闭。
我有一个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]
类型时出错。
解决这个问题的办法是什么?有比我现在做的更好的方法吗?这对我来说似乎是最干净的,但我愿意接受建议。
3条答案
按热度按时间tcbh2hod1#
方法NestedArray[T].UnmarshalJSON递归调用自身。内部调用抛出一个错误,因为它期望
bytes
中的JSON对象,但它收到了一个JSON数组。通过解编为[]T
而不是NestedArray[T]
进行修复。与错误无关,方法NestedArray[T].UnmarshalJSON执行了一些不必要的编码和解码。使用json.RawMessage修复。
下面是包含两个修复的代码:
Run the code on the playground!。
llew8vvj2#
我让 final unmarshal使用一个中间变量,而不是方法的接收者。
41zrol4v3#
遍历JSON查找数组。解码每个数组元素并附加到接收器。
https://go.dev/play/p/lLFjJpr404W
这个问题的标题是“将结构解组为切片的泛型类型别名”,但这里没有问题中的类型别名。这个问题更好的标题是“将结构解组为切片的泛型类型定义”。