نکات دقیق رمزگشایی JSON
رمزگشایی در آرایههای Go
هنگامی که یک آرایه JSON را در یک آرایه Go (نه slice) رمزگشایی میکنید، چند رفتار مهم وجود دارد که باید از آنها آگاه باشید:
- اگر آرایه Go کوچکتر از آرایه JSON باشد، عناصر اضافی آرایه JSON به طور خاموشی دور ریخته میشوند.
- اگر آرایه Go بزرگتر از آرایه JSON باشد، عناصر اضافی آرایه Go با مقادیر صفر خود تنظیم میشوند.
به عنوان مثال:
js := `[1, 2, 3]` var tooShortArray [2]int err := json.NewDecoder(strings.NewReader(js)).Decode(&tooShortArray) if err != nil { log.Fatal(err) } var tooLongArray [4]int err = json.NewDecoder(strings.NewReader(js)).Decode(&tooLongArray) if err != nil { log.Fatal(err) } fmt.Printf("tooShortArray: %v\n", tooShortArray) fmt.Printf("tooLongArray: %v\n", tooLongArray)
چاپ خواهد شد:
tooShortArray: [1 2] tooLongArray: [1 2 3 0]
رمزگشایی جزئی JSON
اگر مقدار زیادی ورودی JSON برای پردازش دارید و فقط به بخش کوچکی از آن نیاز دارید، اغلب میتوانید از نوع json.RawMessage برای کمک به حل این مشکل استفاده کنید. به عنوان مثال:
// Let's say that the only thing we're interested in is processing the "genres" array in // the following JSON object js := `{"title": "Top Gun", "genres": ["action", "romance"], "year": 1986}` // Decode the JSON object to a map[string]json.RawMessage type. The json.RawMessage // values in the map will retain their original, undecoded, JSON values. var m map[string]json.RawMessage err := json.NewDecoder(strings.NewReader(js)).Decode(&m) if err != nil { log.Fatal(err) } // We can then access the JSON "genres" value from the map and decode it as normal using // the json.Unmarshal() function. var genres []string err = json.Unmarshal(m["genres"], &genres) if err != nil { log.Fatal(err) } fmt.Printf("genres: %v\n", genres)
این کد چاپ خواهد شد:
genres: [action romance]
در این مثال ساده، استفاده از json.RawMessage کار زیادی برای ما ذخیره نمیکند. اما اگر نیاز به پردازش یک شیء JSON با دهها یا صدها جفت کلید-مقدار دارید و فقط به چند مورد از آنها نیاز دارید، استفاده از این رویکرد میتواند تایپ زیادی را برای شما ذخیره کند.
رمزگشایی در نوع any
امکان رمزگشایی مقادیر JSON در نوع any وجود دارد. وقتی این کار را انجام میدهید، مقدار زیرینی که نوع any نگه میدارد به نوع مقدار JSON که رمزگشایی میشود بستگی دارد.
| نوع JSON | ⇒ | نوع زیرین Go از any |
|---|---|---|
| JSON boolean | ⇒ | bool |
| JSON string | ⇒ | string |
| JSON number | ⇒ | float64 |
| JSON array | ⇒ | []any |
| JSON object | ⇒ | map[string]any |
| JSON null | ⇒ | nil |
رمزگشایی در نوع any میتواند در شرایط زیر مفید باشد:
- شما از قبل نمیدانید دقیقاً چه چیزی را رمزگشایی میکنید.
- نیاز به رمزگشایی آرایههای JSON دارید که حاوی آیتمهایی با انواع JSON مختلف هستند.
- جفت کلید-مقدار در یک شیء JSON همیشه حاوی مقادیری با نوع JSON یکسان نیست.
به عنوان مثال، کد زیر را در نظر بگیرید:
// This JSON array contains both JSON string and JSON boolean types. js := `["foo", true]` // Decode the JSON into a []any slice. var s []any err := json.NewDecoder(strings.NewReader(js)).Decode(&s) if err != nil { log.Fatal(err) } // The first value in the slice will have the underlying Go type string, the second will // have the underlying Go type bool. We can then type assert them and print them out // the values along with their underlying type. fmt.Printf("item: 0; type: %T; value: %v\n", s[0], s[0].(string)) fmt.Printf("item: 1; type: %T; value: %v\n", s[1], s[1].(bool))
این کد چاپ خواهد شد:
item: 0; type: string; value: foo item: 1; type: bool; value: true
رمزگشایی یک عدد JSON در نوع any
همانطور که در جدول بالا نشان داده شده است، وقتی یک عدد JSON را در نوع any رمزگشایی میکنید، مقدار نوع زیرین float64 خواهد داشت — حتی اگر در JSON اصلی یک عدد صحیح باشد. به عنوان مثال:
js := `10` // This JSON number is an integer. var n any err := json.NewDecoder(strings.NewReader(js)).Decode(&n) if err != nil { log.Fatal(err) } fmt.Printf("type: %T; value: %v\n", n, n)
چاپ خواهد شد:
type: float64; value: 10
اگر میخواهید مقدار را به صورت عدد صحیح (به جای float64) دریافت کنید، باید قبل از رمزگشایی متد UseNumber() را روی نمونه json.Decoder خود فراخوانی کنید. این کار باعث میشود تمام اعداد JSON به نوع زیرین json.Number به جای float64 رمزگشایی شوند.
سپس نوع json.Number متد Int64() را ارائه میدهد که میتوانید آن را برای دریافت عدد به صورت int64 فراخوانی کنید، یا متد String() برای دریافت عدد به صورت string. به عنوان مثال:
js := `10` var n any dec := json.NewDecoder(strings.NewReader(js)) dec.UseNumber() // Before using the decoder, call the UseNumber() method on it. err := dec.Decode(&n) if err != nil { log.Fatal(err) } // Type assert the any value to a json.Number, and then call the Int64() method // to get the number as a Go int64. nInt64, err := n.(json.Number).Int64() if err != nil { log.Fatal(err) } // Likewise, you can use the String() method to get the number as a Go string. nString := n.(json.Number).String() fmt.Printf("type: %T; value: %v\n", n, n) fmt.Printf("type: %T; value: %v\n", nInt64, nInt64) fmt.Printf("type: %T; value: %v\n", nString, nString)
این کد چاپ خواهد شد:
type: json.Number; value: 10 type: int64; value: 10 type: string; value: 10
دستورالعملهای برچسب struct
استفاده از برچسب struct json:"-" روی یک فیلد struct باعث میشود هنگام رمزگشایی JSON نادیده گرفته شود، حتی اگر ورودی JSON حاوی جفت کلید-مقدار مربوطه باشد. به عنوان مثال:
js := `{"name": "alice", "age": 21}` var person struct { Name string `json:"name"` Age int32 `json:"-"` } err := json.NewDecoder(strings.NewReader(js)).Decode(&person) if err != nil { log.Fatal(err) } fmt.Printf("%+v", person)
چاپ خواهد شد:
{Name:alice Age:0}
دستورالعملهای برچسب struct omitzero و omitempty هیچ تأثیری بر رفتار رمزگشایی JSON ندارند.