首頁 > 後端開發 > Golang > 主體

將結構解組為切片的通用類型定義

王林
發布: 2024-02-06 08:54:04
轉載
1099 人瀏覽過

將結構解組為切片的通用類型定義

問題內容

我有一個 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] 類型時發生錯誤。

有什麼辦法可以解決這個問題?有比我現在正在做的更好的方法嗎?這對我來說似乎是最乾淨的,但我願意接受建議。


正確答案


方法 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
}
登入後複製

在 Playground 上運行程式碼!

以上是將結構解組為切片的通用類型定義的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:stackoverflow.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板