Kita boleh menggunakan Encoder.Encode() dan Marshal() bagi pengekodan/pakej json untuk melaksanakan pensirilan Json, dan menggunakan Decoder.Decode() dan Unmarshal() untuk melaksanakan penyahserilan Json.
Contohnya adalah seperti berikut
package main import ( "encoding/json" "os" ) type Metric struct { Name string `json:"name"` Value int64 `json:"value"` } func main() { _ = json.NewEncoder(os.Stdout).Encode( []*Metric{ {"vv", 12}, {"tz", 9}, {"ss", 82}, }, ) }
Output
[{"name":"vv","value":12},{"name":"tz","value":9},{"name":"ss","value":82}]
Ambil struktur di atas Metrik sebagai contoh, ia mewakili penunjuk statistik. Di mana Nama ialah nama penunjuk dan Nilai ialah nilai penunjuk.
Andaikan bahawa apabila program menerima data Json luaran, terdapat situasi di mana nilai penunjuk adalah nombor titik terapung, seperti yang ditunjukkan di bawah.
func main() { var metric Metric err := json.Unmarshal([]byte(`{"name": "tq", "value": 13.14}`), &metric) if err != nil { panic(err) } fmt.Println(metric) }
Memandangkan medan Nilai yang ditakrifkan dalam struktur Metrik ialah int64, penyahserialisasian data Json akan mendapat ralat berikut
panic: json: cannot unmarshal number 13.14 into Go struct field Metric.value of type int64
Bagaimanakah masalah ini harus dikendalikan? Bolehkah kita menangani situasi ini tanpa mengubah definisi struktur asal.
Dalam pengekodan/json, terdapat dua antara muka yang sangat penting.
// Marshaler is the interface implemented by types that // can marshal themselves into valid JSON. type Marshaler interface { MarshalJSON() ([]byte, error) } // Unmarshaler is the interface implemented by types // that can unmarshal a JSON description of themselves. // The input can be assumed to be a valid encoding of // a JSON value. UnmarshalJSON must copy the JSON data // if it wishes to retain the data after returning. // // By convention, to approximate the behavior of Unmarshal itself, // Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op. type Unmarshaler interface { UnmarshalJSON([]byte) error }
如果类型 T 实现了 Marshaler 或 Unmarshaler 接口方法,就能自定义了序列化和反序列化规则。
有了这个理解基础,那么对于上文中的问题,我们很自然地想到,让 Metric 结构体实现UnmarshalJSON()。
那么首先想到最简单的方式,是引入一个临时结构体:让其 Value 字段定义为 float64,其他字段维持和原结构体一致。
func (u *Metric) UnmarshalJSON(data []byte) error { type tmp struct { Name string `json:"name"` Value float64 `json:"value"` } t := &tmp{ Name: u.Name, Value: float64(u.Value), } err := json.Unmarshal(data, t) if err != nil { return nil } // 注意 Unmarshaler 接口定义的以下规则: // UnmarshalJSON must copy the JSON data // if it wishes to retain the data after returning. u.Name = t.Name u.Value = int64(t.Value) return nil }
这样做能够解决我们的问题,但并不优雅。
我们可以使用结构体的继承。这样,当结构体存在大量字段时,我们仅定义需要更改的字段即可。
func (u *Metric) UnmarshalJSON(data []byte) error { t := &struct { Value float64 `json:"value"` *Metric }{ Value: float64(u.Value), Metric: u, } if err := json.Unmarshal(data, &t); err != nil { return err } u.Value = int64(t.Value) return nil }
不过这样子会引出新的问题:继承原结构体的新 strcut 同样会继承原结构体的方法(UnmarshalJSON() 方法),这将无限循环,最终导致堆栈溢出。
fatal error: stack overflow
最佳解决方案是以结构体新类型,让新类型获得原始结构体的所有字段属性,但是却不会继承原有结构体的方法。
func (u *Metric) UnmarshalJSON(data []byte) error { type AliasMetric Metric t := &struct { Value float64 `json:"value"` *AliasMetric }{ Value: float64(u.Value), AliasMetric: (*AliasMetric)(u), } if err := json.Unmarshal(data, &t); err != nil { return err } u.Value = int64(t.Value) return nil }
这样,就完美解决了我们的问题。
在 Json 数据的处理中,我们可以通过为对象实现 Marshaler 或 Unmarshaler 接口方法,从而制定我们想要的序列化和反序列化规则。
本文通过一个简单的示例,展示了如何通过实现结构体的 UnmarshalJSON() 方法,而达到反序列化时更改字段属性的目的。
同理,我们可以为结构体定义 MarshalJSON() 方法,制定序列化规则,这就留给读者自行尝试了。
Atas ialah kandungan terperinci Pergi peraturan siri Json tersuai. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!