Heim  >  Artikel  >  Backend-Entwicklung  >  Gehen Sie zu benutzerdefinierten Json-Serialisierungsregeln

Gehen Sie zu benutzerdefinierten Json-Serialisierungsregeln

Go语言进阶学习
Go语言进阶学习nach vorne
2023-07-24 15:18:391275Durchsuche
Während des Entwicklungsprozesses verwenden wir häufig JSON als Datenübertragungsformat. Dies ist untrennbar mit der Kodierung und Dekodierung von JSON-Daten verbunden. In Go bietet das Paket „encoding/json“ diese Funktionen.

Wir können Encoder.Encode() und Marshal() des Encoding/JSON-Pakets verwenden, um die Json-Serialisierung zu implementieren, und Decoder.Decode() und Unmarshal() verwenden, um die Json-Deserialisierung zu implementieren.

Das Beispiel ist wie folgt

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},
  },
 )
}

Ausgabe

[{"name":"vv","value":12},{"name":"tz","value":9},{"name":"ss","value":82}]

Stellen Sie eine Frage

Nehmen Sie die obige Strukturmetrik als Beispiel, sie stellt einen statistischen Indikator dar. Dabei ist Name der Indikatorname und Wert der Indikatorwert.

Angenommen, wenn das Programm externe JSON-Daten empfängt, gibt es eine Situation, in der der Indikatorwert eine Gleitkommazahl ist, wie unten gezeigt.

func main() {
 var metric Metric
 err := json.Unmarshal([]byte(`{"name": "tq", "value": 13.14}`), &metric)
 if err != nil {
  panic(err)
 }
 fmt.Println(metric)
}

Da das in der Metric-Struktur definierte Wertfeld int64 ist, wird bei der Deserialisierung von Json-Daten der folgende Fehler angezeigt:

panic: json: cannot unmarshal number 13.14 into Go struct field Metric.value of type int64

Wie sollte dieses Problem behandelt werden? Können wir diese Situation bewältigen, ohne die ursprüngliche Strukturdefinition zu ändern?

Lösung

In Encoding/JSON gibt es zwei sehr wichtige Schnittstellen.

// 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() 方法,制定序列化规则,这就留给读者自行尝试了。

Das obige ist der detaillierte Inhalt vonGehen Sie zu benutzerdefinierten Json-Serialisierungsregeln. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:Go语言进阶学习. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen