JSON به ساختار Go
تولید struct های Go از JSON
ورودی JSON
خروجی Go
تبدیل JSON به ساختار Go چیست؟
تبدیل JSON به Go دادههای JSON خام را به تعریف نوعهای Go تبدیل میکند که با پکیج encoding/json در کتابخانه استاندارد Go کار میکنند. Go یک زبان با نوعبندی ایستا است، بنابراین هر فیلد JSON به یک فیلد struct متناظر با نوع صحیح و یک struct tag نیاز دارد که به serializer بگوید کدام کلید JSON را نگاشت کند. نوشتن این تعریفها بهصورت دستی برای پاسخهای API بزرگ یا عمیقاً تودرتو وقتگیر و مستعد خطاست.
پکیج encoding/json که از Go 1.0 در مشخصات زبان تعریف شده، از reflection برای نگاشت کلیدهای JSON به فیلدهای struct صادرشده استفاده میکند. این نگاشت بهصورت پیشفرض case-insensitive است، اما استفاده صریح از json struct tag روش استاندارد است زیرا ابهام را از بین میبرد و به شما اجازه میدهد قرارداد PascalCase در Go را حفظ کنید در حالی که JSON همچنان به صورت camelCase یا snake_case باقی میماند. یک مبدل این فرایند را خودکار میکند: JSON شما را میخواند، نوعهای Go را از مقادیر استنتاج میکند، و تعریف struct با tag های صحیح را تولید میکند.
سیستم نوع Go بهخوبی به JSON نگاشت میشود. رشتهها به string، مقادیر boolean به bool، اعداد صحیح به int، و اعداد اعشاری به float64 تبدیل میشوند. اشیاء JSON تودرتو به struct های جداگانه با نام مخصوص تبدیل میشوند، و آرایهها به slice تبدیل میشوند. یک استثنا وجود دارد: null — Go هیچ نوع nullable جهانی ندارد، بنابراین مقادیر null معمولاً به نوعهای pointer (*string، *int) یا interface{} تبدیل میشوند. یک مولد همه اینها را در چند میلیثانیه مدیریت میکند.
چرا از مبدل JSON به Go استفاده کنیم؟
تعریف دستی struct های Go از JSON به معنای شمارش آکولادها، حدس زدن نوعها، و بازنویسی tag ها با هر بار تغییر API است. یک مبدل از این دردسر جلوگیری میکند.
موارد استفاده JSON به ساختار Go
جدول نگاشت نوع JSON به Go
پکیج encoding/json هنگام نگاشت مقادیر JSON به نوعهای Go از قوانین خاصی پیروی میکند. جدول زیر نگاشت پیشفرض و گزینههای رایج جایگزین را نشان میدهد. ستون «پیشفرض» همان چیزی است که اکثر مولدها تولید میکنند. ستون «جایگزین» نوعهایی را نشان میدهد که ممکن است بر اساس نیازهایتان انتخاب کنید، مانند int64 برای شناسههای بزرگ یا نوعهای pointer برای فیلدهای nullable.
| نوع JSON | مقدار نمونه | پیشفرض | جایگزین |
|---|---|---|---|
| string | "hello" | string | string |
| number (integer) | 42 | int | int64 |
| number (float) | 3.14 | float64 | float64 |
| boolean | true | bool | bool |
| null | null | interface{} | *string / *int |
| object | {"k": "v"} | struct | struct |
| array of strings | ["a", "b"] | []string | []string |
| array of objects | [{"id": 1}] | []Item | []Item |
| mixed array | [1, "a"] | []interface{} | []interface{} |
مرجع json struct tag در Go
struct tag ها کنترل میکنند که encoding/json چگونه فیلدها را سریالسازی و deserialization میکند. json tag تا حد زیادی رایجترین است، اما میتوانید آن را با tag های دیگر (db، yaml، xml) روی همان فیلد ترکیب کنید. دستور زبان tag یک رشته محدودشده با backtick پس از نوع فیلد است. در اینجا گزینههای json tag که encoding/json پشتیبانی میکند آمده است.
| Tag | رفتار | موارد استفاده |
|---|---|---|
| json:"name" | Maps struct field to JSON key "name" | Always generated |
| json:"name,omitempty" | Omits field from output if zero value | Optional fields |
| json:"-" | Field is never serialized or deserialized | Internal fields |
| json:"name,string" | Encodes int/bool as JSON string | String-encoded numbers |
json.Unmarshal در مقابل json.NewDecoder
Go دو روش برای رمزگشایی JSON ارائه میدهد: json.Unmarshal برای slice های بایت و json.NewDecoder برای stream های io.Reader. هر دو از همان قوانین struct tag استفاده میکنند، اما در زمان استفاده با هم تفاوت دارند.
نمونه کد
این مثالها نشان میدهند چگونه از struct های Go تولیدشده از JSON در برنامههای واقعی استفاده کنید، بهعلاوه نحوه تولید آنها از زبانهای دیگر. مثالهای Go از encoding/json در کتابخانه استاندارد استفاده میکنند.
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
IsActive bool `json:"is_active"`
Tags []string `json:"tags"`
}
func main() {
data := []byte(`{"id":1,"name":"Alice","email":"alice@example.com","is_active":true,"tags":["admin","editor"]}`)
var user User
if err := json.Unmarshal(data, &user); err != nil {
panic(err)
}
fmt.Println(user.Name) // → Alice
fmt.Println(user.Tags) // → [admin editor]
}// JSON response from API — this is the shape you'd convert to Go:
const res = await fetch("https://api.example.com/users/1");
const user = await res.json();
// user → { "id": 1, "name": "Alice", "email": "alice@example.com" }
// Equivalent Go struct:
// type User struct {
// ID int `json:"id"`
// Name string `json:"name"`
// Email string `json:"email"`
// }import json
def json_to_go(data: dict, name: str = "Root") -> str:
lines = [f"type {name} struct {{"]
type_map = {str: "string", int: "int", float: "float64", bool: "bool"}
for key, value in data.items():
go_type = type_map.get(type(value), "interface{}")
field = key.title().replace("_", "") # snake_case → PascalCase
lines.append(f'\t{field} {go_type} `json:"{key}"`')
lines.append("}")
return "\n".join(lines)
data = json.loads('{"user_name": "Alice", "age": 30, "verified": true}')
print(json_to_go(data))
# type Root struct {
# UserName string `json:"user_name"`
# Age int `json:"age"`
# Verified bool `json:"verified"`
# }package main
import (
"encoding/json"
"fmt"
"strings"
)
type Event struct {
Type string `json:"type"`
Payload string `json:"payload"`
}
func main() {
stream := strings.NewReader(`{"type":"click","payload":"btn-1"}
{"type":"scroll","payload":"page-2"}`)
dec := json.NewDecoder(stream)
for dec.More() {
var ev Event
if err := dec.Decode(&ev); err != nil {
break
}
fmt.Printf("%s: %s\n", ev.Type, ev.Payload)
}
// → click: btn-1
// → scroll: page-2
}