Saya mempunyai API yang biasanya mengembalikan tatasusunan sebagai objek yang mengandungi tatasusunan. Ambil contoh berikut sebagai contoh:
<code>{ "items": { "number": 3, "item": [ { ... } // Not relevant ] } } </code>
API melakukan ini di berpuluh-puluh tempat, setiap kali dengan nama yang berbeza. Ini dijamin berlaku dengan hanya dua kekunci: satu daripadanya ialah number
dan satu lagi ialah tatasusunan.
Ini menjadikan struktur yang terhasil agak tidak menyenangkan untuk digunakan, kerana anda perlu sentiasa menavigasi melalui tahap medan yang tidak perlu.
Saya pada asasnya mahu antara muka Go saya berpura-pura ia mempunyai format ini:
<code>{ "items": [ { ... } // Not relevant ] } </code>
Satu pilihan ialah menulis fungsi UnmarshalJSON
tersuai untuk setiap kejadian, tetapi ini kelihatan menyusahkan, terutamanya memandangkan ia muncul dalam hampir setiap struktur. Penyelesaian yang saya buat adalah jenis generik yang boleh mengendalikannya sendiri.
Percubaan saya sekarang adalah seperti berikut:
<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>
Cara penggunaan adalah seperti berikut:
<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>
menghasilkan ralat berikut:
json: cannot unmarshal array into Go struct field Root.items of type map[string]interface{}
UnmarshalJSON
代码的最大部分似乎是正确的,因为我的调试器向我显示 sliceVal
正是我所期望的。解组回 NestedArray[T]
Bahagian terbesar kod nampaknya betul, kerana penyahpepijat saya menunjukkan kepada saya bahawa sliceVal
adalah seperti yang saya jangkakan. Ralat menyahmarsha kembali ke jenis NestedArray[T]
.
Adakah cara untuk menyelesaikan masalah ini? Adakah terdapat cara yang lebih baik daripada apa yang saya lakukan sekarang? Ini nampaknya paling bersih kepada saya, tetapi saya terbuka kepada cadangan.
Kaedah NestedArray[T].UnmarshalJSON memanggil dirinya secara rekursif. Panggilan dalaman menimbulkan ralat kerana ia memerlukan bytes
中的 JSON 对象,但它收到了一个 JSON 数组。通过解组到 []T
而不是 NestedArray[T]
untuk dibetulkan.
Tidak berkaitan dengan ralat, kaedah NestedArray[T].UnmarshalJSON melakukan beberapa pengekodan dan penyahkodan yang tidak perlu. Gunakan json.RawMessage untuk membetulkannya.
Berikut ialah kod dengan kedua-dua pembetulan:
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 }
Jalankan kod di Taman Permainan! .
Atas ialah kandungan terperinci Takrif jenis generik untuk unmarshalling struct ke dalam kepingan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!