エンコーディング/json パッケージの Encoder.Encode() と Marshal() を使用して Json シリアル化を実装し、Decoder.Decode() と Unmarshal を使用できます。 () Jsonの逆シリアル化を実装します。
#例は次のとおりです。
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}, }, ) }
出力
[{"name":"vv","value":12},{"name":"tz","value":9},{"name":"ss","value":82}]
上記の構造メトリックを例として取り上げます。これは統計指標を表します。ここで、Name はインジケーター名、Value はインジケーターの値です。
プログラムが外部 Json データを受信するときに、以下に示すようにインジケーターの値が浮動小数点数である状況があるとします。
func main() { var metric Metric err := json.Unmarshal([]byte(`{"name": "tq", "value": 13.14}`), &metric) if err != nil { panic(err) } fmt.Println(metric) }
メトリック構造で定義されている値フィールドは int64 であるため、Json データの逆シリアル化により次のエラーが発生します
panic: json: cannot unmarshal number 13.14 into Go struct field Metric.value of type int64
Howこの問題は対処すべきでしょうか?元の構造定義を変更せずにこの状況に対処できるでしょうか。
エンコーディング/json には、2 つの非常に重要なインターフェイスがあります。
// 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() 方法,制定序列化规则,这就留给读者自行尝试了。
以上がGo カスタム Json シリアル化ルールの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。