Takrif jenis generik untuk unmarshalling struct ke dalam kepingan

王林
Lepaskan: 2024-02-06 08:54:04
ke hadapan
1082 orang telah melayarinya

Takrif jenis generik untuk unmarshalling struct ke dalam kepingan

Kandungan soalan

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>
Salin selepas log masuk

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>
Salin selepas log masuk

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>
Salin selepas log masuk

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>
Salin selepas log masuk

menghasilkan ralat berikut:

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

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.


Jawapan betul


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
}
Salin selepas log masuk

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!

sumber:stackoverflow.com
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!