1. encoding/json اسٹینڈرڈ لائبریری کا جائزہ

Go زبان میں encoding/json کتابخانہ موجود ہے جو JSON ڈیٹا فارمیٹ کو سنبھالنے کے لئے تھوڑی سے زیادہ نہایت قوی ہے۔ اس لائبریری کے ذریعے، آپ Go ڈیٹا پرقسم کو JSON فارمیٹ میں تبدیل کرسکتے ہیں (سیریلائزیشن) یا JSON ڈیٹا کو Go ڈیٹا کی قسم میں تبدیل کرسکتے ہیں (ڈیسیریلائزیشن)۔ یہ لائبریری کوڈ کوڈ کرنے، کوڈ کوڈنگ، اسٹریمنگ IO اور کسٹم JSON پارسنگ منطق کی حمایت فراہم کرتی ہے۔

اس لائبریری میں سب سے اہم ڈیٹا ٹائپس اور فنکشنز میں شامل ہیں:

  • Marshal اور MarshalIndent: Go ڈیٹا ٹائپس کو JSON سٹرنگ میں سیریلائز کرنے کے لئے استعمال ہوتے ہیں۔
  • Unmarshal: JSON سٹرنگز کو Go ڈیٹا ٹائپس میں ڈیسیریلائز کرنے کے لئے استعمال ہوتا ہے۔
  • Encoder اور Decoder: JSON ڈیٹا کا اسٹریمنگ IO کے لئے استعمال ہوتے ہیں۔
  • Valid: دی گئی سٹرنگ کا چیک کرنے کے لئے استعمال ہوتا ہے کہ کیا یہ درست JSON فارمیٹ ہے یا نہیں۔

ہم اگلے چیپٹروں میں خصوصی طور پر ان فنکشنز اور ٹائپس کے استعمال کا سیکھیں گے۔

2. Go ڈیٹا س۟ٹرکچر کو JSON میں تبدیل کرنا

2.1 json.Marshal کا استعمال

json.Marshal ایک فنکشن ہے جو Go ڈیٹا ٹائپس کو JSON سٹرنگ میں سیریلائز کرتا ہے۔ یہ Go زبان سے ڈیٹا ٹائپس کو ان پٹ لیندہ ہے، انہیں JSON فارمیٹ میں تبدیل کرتا ہے، اور بائٹ سلائس کے ساتھ ساتھ ممکنہ خرابیوں کے ساتھ واپس کرتا ہے۔

یہاں ایک سادہ مثال دی گئی ہے جو دکھاتی ہے کہ کیسے ایک Go سٹرکٹ کو JSON سٹرنگ میں تبدیل کیا جاتا ہے:

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

//... بقایا کود اور نتیجہ کی پروڈکشن بھول گئیں ...

سٹرکٹ کے علاوہ، json.Marshal فنکشن دوسرے ڈیٹا ٹائپس جیسے کہ میپ اور سلائس کو بھی سیریلائز کرسکتا ہے۔ ذیل میں map[string]interface{} اور slice کا استعمال کرتے ہوئے مثالیں دی گئی ہیں:

// نقل کو JSON میں تبدیل کریں
myMap := map[string]interface{}{
    "name": "Bob",
    "age":  25,
}
// ... خرابی کا ہنڈلنگ اور نتیجہ کی پروڈکشن بھول گئیں ...

// سلائس کو JSON میں تبدیل کریں
mySlice := []string{"Apple", "Banana", "Cherry"}
// ... خرابی کا ہنڈلنگ اور نتیجہ کی پروڈکشن بھول گئیں ...

2.2 سٹرکٹ ٹیگز

Go میں، سٹرکٹ ٹیگز سٹرکٹ فیلڈز کے لئے میٹا ڈیٹا فراہم کرنے کے لئے استعمال ہوتے ہیں، جو JSON سیریلائزیشن کے عمل کو کنٹرول کرتے ہیں۔ اس میں سب سے عام استعمالات میں فیلڈ کا نام بدلنا، فیلڈز کو نظرانداز کرنا، اور مشروط سیریلائزیشن شامل ہے۔

مثال کے طور پر، آپ تاگ json:"<name>" کا استعمال کر کے JSON فیلڈ کا نام مخصوص کرسکتے ہیں:

type Animal struct {
    SpeciesName string `json:"species"`
    Description string `json:"desc,omitempty"`
    Tag         string `json:"-"` // "-" ٹیگ شامل کرنا بتاتا ہے کہ یہ فیلڈ سیریلائز نہیں ہوگا
}

اوپر دی گئی مثال میں، Tag فیلڈ کے سامنے json:"-" ٹیگ مشخص کرتا ہے کہ Tag فیلڈ کو json.Marshal کو اسے نظرانداز کرنے کا حکم دیا گیا ہے۔ Description فیلڈ کے لئے omitempty آپشن یہ بتاتا ہے کہ اگر فیلڈ خالی ہے (صفر قیمت، مثال کے طور پر خالی سٹرنگ) تو یہ سیریلائزڈ JSON میں شامل نہیں ہوگا۔

نیچے ایک مکمل مثال ہے جو سٹرکٹ ٹیگز کا استعمال کرتی ہے:

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

type Animal struct {
    SpeciesName string `json:"species"`
    Description string `json:"desc,omitempty"`
    Tag         string `json:"-"`
}

func main() {
    animal := Animal{
        SpeciesName: "African Elephant",
        Description: "A large mammal with a trunk and tusks.",
        Tag:         "endangered", // یہ فیلڈ سیریلائزڈ JSON میں شامل نہیں ہوگا
    }
    jsonData, err := json.Marshal(animal)
    if err != nil {
        log.Fatalf("JSON marshaling failed: %s", err)
    }
    fmt.Println(string(jsonData)) // Output: {"species":"African Elephant","desc":"A large mammal with a trunk and tusks."}
}

اس طرح، آپ یہ یقینی بنا سکتے ہیں کہ ڈیٹا سچھائی کو یقینی بنانے کے ساتھ ساتھ مختلف سیریلائزیشن کی ضروریات کو مرنجکتر سے ہینڈل کیا جا سکتا ہے۔

3. JSON کو Go ڈیٹا سچھائی میں ڈیسیریلائز کریں

3.1 json.Unmarshal کا استعمال

json.Unmarshal فنکشن ہمیں یہ اجازت دیتا ہے کہ ہم JSON سٹرنگز کو گو دیتا ساختوں جیسے کے structures میں پارس کر سکیں، میپس وغیرہ۔ json.Unmarshal کا استعمال کرنے کے لئے، ہمیں پہلے ایسا گو دیتا ساختون تعین کرنا ہوتا ہے جو کے JSON ڈیٹا کے مطابق ہو۔

مان لیں ہمارے پاس مندرجہ ذیل JSON ڈیٹا ہے:

{
    "name": "Alice",
    "age": 25,
    "emails": ["[email protected]", "[email protected]"]
}

اس ڈیٹا کو گو سٹرکچر میں پارس کرنے کے لئے، ہمیں مندرجہ ذیل میچنگ سٹرکچر ڈیفائن کرنا ہوتا ہے:

type User struct {
    Name   string   `json:"name"`
    Age    int      `json:"age"`
    Emails []string `json:"emails"`
}

اب ہم اسے ڈیسیریالائز کرنے کے لئے json.Unmarshal کا استعمال کر سکتے ہیں:

import (
    "encoding/json"
    "fmt"
)

func main() {
    jsonData := `{
        "name": "Alice",
        "age": 25,
        "emails": ["[email protected]", "[email protected]"]
    }`

    var user User
    err := json.Unmarshal([]byte(jsonData), &user)
    if err != nil {
        fmt.Println("Error unmarshalling JSON:", err)
        return
    }

    fmt.Printf("User: %+v\n", user)
}

موجودہ مثال میں، ہم نے json.Unmarshal کو منسلک فیلڈس کے سٹرکچر فیلڈس کے میپنگ کے بارے میں بتانے کے لئے json:"name" جیسے ٹیگ استعمال کئے ہیں۔

3.2 متغیرپر ریاست

کبھی کبھار، ہمیں پارس کرنے کے لئے ہم کو جاننے کی ضرورت نہیں ہوتی کہ JSON سٹرکچر پہلے سے معلوم ہو یا جو JSON ڈیٹا کا سٹرکچر دائمی طور پر تبدیل ہو جاتا ہے۔ ایسے مواقع میں، ہم interface{} یا json.RawMessage کا استعمال کر سکتے ہیں۔

interface{} کا استعمال ہمیں بغیر سٹرکچر جاننے کے پارس کرنے کی اجازت دیتا ہے:

func main() {
    jsonData := `{
        "name": "Alice",
        "details": {
            "age": 25,
            "job": "Engineer"
        }
    }`

    var result map[string]interface{}
    json.Unmarshal([]byte(jsonData), &result)

    fmt.Println(result)

    // ٹائپ کی یقینیت، استعمال سے پہلے یقینی ٹائپ میچنگ یقینی بنائیں
    name := result["name"].(string)
    fmt.Println("Name:", name)
    details := result["details"].(map[string]interface{})
    age := details["age"].(float64) // نوٹ: interface{} میں نمبرز کو float64 کے طور پر ٹریٹ کیا جاتا ہے
    fmt.Println("Age:", age)
}

json.RawMessage کا استعمال ہمیں اصلی JSON کو رکھتے ہوئے اس کے حصوں کو منتخب کرتے ہوئے پارس کرنے کی اجازت دیتا ہے:

type UserDynamic struct {
    Name    string          `json:"name"`
    Details json.RawMessage `json:"details"`
}

func main() {
    jsonData := `{
        "name": "Alice",
        "details": {
            "age": 25,
            "job": "Engineer"
        }
    }`

    var user UserDynamic
    json.Unmarshal([]byte(jsonData), &user)

    var details map[string]interface{}
    json.Unmarshal(user.Details, &details)

    fmt.Println("Name:", user.Name)
    fmt.Println("Age:", details["age"])
    fmt.Println("Job:", details["job"])
}

یہ ترتیب مختلف قسم کے ڈیٹا کو ہینڈل کرنے کے لئے مفید ہوتا ہے، اور لوگوں کو اختیارات کے ساتھ ڈیٹا ہینڈل کرنے کا مواقع دیتا ہے۔

4 نیسٹڈ سٹرکچر اور ایرے کا پارس

4.1 نیسٹڈ JSON آبجیکٹس

عام JSON ڈیٹا عموماً فلیٹ نہیں ہوتا، بلکہ نیسٹڈ سٹرکچرز شامل ہوتے ہیں۔ گو میں ہم اس صورتحال کا مقابلہ کر سکتے ہیں جب ہم نیسٹڈ سٹرکچرز کو ڈیفائن کرتے ہیں۔

سمجھیں ہمارے پاس مندرجہ ذیل نیسٹڈ JSON ہے:

{
    "name": "Bob",
    "contact": {
        "email": "[email protected]",
        "address": "123 Main St"
    }
}

ہم گو سٹرکچر مندرجہ ذیل طریقے سے ڈیفائن کر سکتے ہیں:

type ContactInfo struct {
    Email   string `json:"email"`
    Address string `json:"address"`
}

type UserWithContact struct {
    Name    string      `json:"name"`
    Contact ContactInfo `json:"contact"`
}

ڈیسیریالائز کرنے کا عمل غیر نیسٹڈ سٹرکچرز کی طرح ہوتا ہے:

func main() {
    jsonData := `{
        "name": "Bob",
        "contact": {
            "email": "[email protected]",
            "address": "123 Main St"
        }
    }`

    var user UserWithContact
    err := json.Unmarshal([]byte(jsonData), &user)
    if err != nil {
        fmt.Println("Error unmarshalling JSON:", err)
    }

    fmt.Printf("%+v\n", user)
}

4.2 JSON Arrays

JSON میں، Arrays ایک عام ڈیٹا سٹرکچر ہیں۔ Go میں، یہ slices کو مطابقتی ہوتے ہیں۔

مندرجہ ذیل JSON array کو دیکھیں:

[
    {"name": "Dave", "age": 34},
    {"name": "Eve", "age": 28}
]

Go میں، ہم مندرجہ ذیل struct اور slice کو منظر عام کرتے ہیں:

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func main() {
    jsonData := `[
        {"name": "Dave", "age": 34},
        {"name": "Eve", "age": 28}
    ]`

    var people []Person
    json.Unmarshal([]byte(jsonData), &people)
    
    for _, person := range people {
        fmt.Printf("%+v\n", person)
    }
}

ایسے طریقے سے، ہم ہر عنصر کو JSON array میں deserialize کر کے Go structs کی ایک slice میں تبدیل کر سکتے ہیں تاکہ انہیں آگے کے استعمال اور رسائی کے لئے استعمال کیا جا سکے۔

5 Error Handling

جب JSON ڈیٹا سے کام کیا جائے، چاہے وہ serialization ہو (جو ڈیٹا کو JSON فارمیٹ میں تبدیل کرنا ہو) یا deserialization (جو JSON کو دوبارہ ڈیٹا میں تبدیل کرنا ہو)، تو خرابیاں پیش آ سکتی ہیں۔ اگلے، ہم معمولی خرابیوں پر غور کریں گے اور ان کا سامنا کرنے کا طریقہ بیان کریں گے۔

5.1 Serialization Error Handling

Serialization errors عام طور پر اس عمل کے دوران پیش آتے ہیں جب struct یا دیگر ڈیٹا ٹائپس کو JSON string میں تبدیل کرنے کے دوران ہوتے ہیں۔ مثال کے طور پر، اگر کسی struct میں غیر قانونی فیلڈز شامل ہوں (جیسے کے چینل ٹائپ یا فنکشن جو JSON میں نہیں دکھایا جا سکتا) تو json.Marshal ایک خرابی واپس دے گا۔

import (
    "encoding/json"
    "fmt"
    "log"
)

type User struct {
    Name string
    Age  int
    // یہاں فرض کریں کے ایک فیلڈ ہے جو serialized نہیں کیا جا سکتا
    // Data chan struct{} // چینلز کو JSON میں نہیں دیکھایا جا سکتا
}

func main() {
    u := User{
        Name: "Alice",
        Age:  30,
        // Data: make(chan struct{}),
    }

    bytes, err := json.Marshal(u)
    if err != nil {
        log.Fatalf("JSON serialization failed: %v", err)
    }
    
    fmt.Println(string(bytes))
}

مثال میں، ہم نے مقصوداً Data فیلڈ کو مقصود سے بند کر دیا ہے۔ اگر ہم اس کو باز نہ کریں تو serialization ناکام ہو گی، اور پروگرام خرابی کو لاگ کریں گے اور اِس کا اجراء ختم ہو جائے گا۔ ان خرابیوں کا سامنا کرنا عموماً خرابیاں چیک کرنا اور ان کے مطابقتی خرابی کا سامنا کرنے کے استراتیجیے (جیسے کے خرابیوں کو لاگ کرنا، معمولی ڈیٹا واپسی کرنا وغیرہ) کو انجام دینے سے ہوتا ہے۔

5.2 Deserialization Error Handling

Deserialization errors اس عمل کے دوران پیش آ سکتی ہیں جب JSON string کو دوبارہ Go struct یا دیگر ڈیٹا ٹائپس میں تبدیل کرتے ہیں۔ مثال کے طور پر، اگر JSON string کا فارمیٹ غلط ہو یا مطابقت نہ کرے موجودہ ٹائپ کے ساتھ، تو json.Unmarshal ایک خرابی واپس دے گا۔

import (
    "encoding/json"
    "fmt"
    "log"
)

func main() {
    var data = []byte(`{"name":"Alice","age":"unknown"}`) // "age" کو ایک integer ہونا چاہئے، لیکن یہاں ایک string فراہم کی گئی ہے
    var u User

    err := json.Unmarshal(data, &u)
    if err != nil {
        log.Fatalf("JSON deserialization failed: %v", err)
    }
    
    fmt.Printf("%+v\n", u)
}

اس کوڈ مثال میں، ہم نے مقصوداً age فیلڈ کے لئے غلط ڈیٹا ٹائپ فراہم کیا ہے (ایک string بجائے متوقع integer)، جس سے json.Unmarshal نے ایک خرابی واپس دی۔ لہذا، ہمیں اس صورت حال کا مناسب سامنا کرنا ہوگا۔ عام طریقہ ہے کہ خرابی کا پیغام لاگ کریں اور سناریو کے مطابق، ممکن ہے کہ خالی آبجیکٹ، ڈیفالٹ قیمت یا خرابی کا پیغام واپس کریں۔

6 Advanced Features and Performance Optimization

6.1 کسٹم مارشل اور غیر مارشل

پہلے سے encoding/json پیکیج جو گو میں ہوتا ہے، جیسن کو تشکیل دیتا ہے اور کھولتا ہے۔ البتہ، ہم json.Marshaler اور json.Unmarshaler انٹرفیس کو پورا کرکے ان عملوں کو خود بھی منظم کرسکتے ہیں۔

import (
    "encoding/json"
    "fmt"
)

type Color struct {
    Red   uint8
    Green uint8
    Blue  uint8
}

func (c Color) MarshalJSON() ([]byte, error) {
    hex := fmt.Sprintf("\"#%02x%02x%02x\"", c.Red, c.Green, c.Blue)
    return []byte(hex), nil
}

func (c *Color) UnmarshalJSON(data []byte) error {
    _, err := fmt.Sscanf(string(data), "\"#%02x%02x%02x\"", &c.Red, &c.Green, &c.Blue)
    return err
}

func main() {
    c := Color{Red: 255, Green: 99, Blue: 71}
    
    jsonColor, _ := json.Marshal(c)
    fmt.Println(string(jsonColor))

    var newColor Color
    json.Unmarshal(jsonColor, &newColor)
    fmt.Println(newColor)
}

یہاں، ہم نے ایک Color قسم کی تعریف کی ہے اور MarshalJSON اور UnmarshalJSON تراکیب دی ہیں تاکہ آر جی بی روشنیاں ہیکساڈیسمل سٹرنگس میں تبدیل کرسکیں اور پھر روشنیے کو واپس آر جی بی کلر میں تبدیل کرسکیں۔

6.2 انکوڈرز اور ڈیکوڈرز

بڑی رقم کے جیسن ڈیٹا سے کام کرنے کے وقت، سیدھے تور پر json.Marshal اور json.Unmarshal استعمال کرنا بڑے پیمانے پر یاد شدید مصرف یا غیر موزون ان پُٹ/آوٹ عملوں کا باعث بنسکتا ہے۔ لہذا، گو میں encoding/json پیکیج انکوڈر اور ڈیکوڈر قسم فراہم کرتا ہے، جو جیسن ڈیٹا کو ایک نگرانفتی طریقے سے پیش کرسکتے ہیں۔

6.2.1 json.Encoder استعمال

json.Encoder سیدھے طور پر جیسن ڈیٹا کو کسی بھی اشیاء کو لکھ سکتا ہے جو io.Writer انٹرفیس پورا کرتا ہے، یعنی آپ جیسن ڈیٹا کو سیدھے طور پر فائل، نیٹ ورک کنکشن، وغیرہ میں ان کوڈ کرسکتے ہیں۔

import (
    "encoding/json"
    "os"
)

func main() {
    users := []User{
        {Name: "Alice", Age: 30},
        {Name: "Bob", Age: 25},
    }
    
    file, _ := os.Create("users.json")
    defer file.Close()
    
    encoder := json.NewEncoder(file)
    if err := encoder.Encode(users); err != nil {
        log.Fatalf("Encoding error: %v", err)
    }
}

6.2.2 json.Decoder استعمال

json.Decoder سیدھے طور پر جیسن ڈیٹا کو کسی بھی اشیاء سے پڑھ سکتا ہے جو io.Reader انٹرفیس پورا کرتا ہے، جیسن کے اشیاء اور ارایئے تلاش کرتا ہے اور انے پارس کرتا ہے۔

import (
    "encoding/json"
    "os"
)

func main() {
    file, _ := os.Open("users.json")
    defer file.Close()
    
    var users []User
    decoder := json.NewDecoder(file)
    if err := decoder.Decode(&users); err != nil {
        log.Fatalf("Decoding error: %v", err)
    }
    
    for _, u := range users {
        fmt.Printf("%+v\n", u)
    }
}

انکوڈرز اور ڈیکوڈرز کے ساتھ ڈیٹا کا فراہمی اور فراہمی کے دورانیہ میں جیسن کا پروسیسنگ کرنے سے، آپ میموری کا استعمال کم کرکے پڑھنے اور پراسسنگ کی کارکردگی کو بہتر بنا سکتے ہیں، جو نیٹ ورک ٹرانسفر یا بڑے فائلز کو ہینڈل کرنے کے لیے خاص طور پر مفید ہے۔