दो कलाबाज़ भाई: Unmarshal और Decode
\n\nयदि आप Go में लिखते हैं और JSON के साथ काम करते हैं, तो देर-सबेर आपके सामने यह विकल्प आता है: json.Unmarshal का उपयोग करें या json.NewDecoder.Decode का? यह प्रश्न इतना लोकप्रिय है कि Stack Overflow पर इसने हज़ारों बार देखा गया है और दर्जनों वोट प्राप्त किए हैं। लेकिन वहाँ आपको जो उत्तर मिलेगा, वह अक्सर "यह कार्य पर निर्भर करता है" तक सीमित होता है।
आज हम केवल एक सारगर्भित उत्तर नहीं देंगे। हम अपनी स्वयं की जाँच करेंगे: दोनों विधियों की आंतरिक कार्यप्रणाली को समझेंगे, बेंचमार्क लिखेंगे, वास्तविक कोड उदाहरण दिखाएंगे, और स्पष्ट नियम बनाएंगे कि कब क्या उपयोग करना चाहिए। तैयार हो जाइए — यह गरमागरम होने वाला है!
\n\nमुख्य बात संक्षेप में: Unmarshal और Decode क्या हैं?
\n\nदोनों उपकरण एक ही काम करते हैं — JSON को Go संरचना में बदलना। लेकिन वे इसे अलग-अलग तरीके से करते हैं।
\n\n- \n
json.Unmarshal— इनपुट के रूप में[]byteऔर एक संरचना का पॉइंटर लेता है। सब कुछ एक साथ: बाइट्स मौजूद हैं, संरचना तैयार है, चलिए शुरू करते हैं। यह "सीधा" काम करता है — मेमोरी में डेटा के पूरे ऐरे को डिसीरियलाइज़ करता है। \n json.NewDecoder.Decode— स्ट्रीम (io.Reader) के साथ काम करता है। आप एक डिकोडर बनाते हैं, उसे एक रीडर (जैसे,os.Fileयाhttp.Response.Body) देते हैं औरDecodeको कॉल करते हैं। डिकोडर डेटा को भागों में पढ़ता है, बिना सब कुछ एक साथ मेमोरी में लोड किए। \n
स्पष्ट रूप से, अंतर स्पष्ट है: बड़े डेटा के लिए — Decoder, छोटे के लिए — Unmarshal। लेकिन शैतान, हमेशा की तरह, विवरण में है।
\n\nआंतरिक कार्यप्रणाली: वे हुड के नीचे कैसे काम करते हैं
\n\njson.Unmarshal — स्नाइपर शॉट
\n\nUnmarshal एक फ़ंक्शन है जो:
\n- \n
- बाइट्स का एक स्लाइस लेता है। \n
- परिणामी संरचना के लिए मेमोरी आवंटित करता है। \n
- एक बार में पूरे JSON को पार्स करता है और संरचना को भरता है। \n
सब कुछ सरल और सुरुचिपूर्ण है। लेकिन एक बारीक बात है: यदि आप Unmarshal में एक बड़ा JSON (जैसे, 100 MB) पास करते हैं, तो पहले आपको उन 100 MB को मेमोरी में पढ़ना होगा। और फिर Unmarshal संरचना के लिए और मेमोरी आवंटित करेगा। परिणाम — RAM की दोगुनी खपत।
\n\njson.NewDecoder.Decode — प्लंबर
\n\nDecoder अलग तरह से काम करता है:
\n- \n
- एक नया डिकोडर बनाया जाता है, जो
io.Readerको लपेटता है। \n - डिकोडर डेटा को बफ़र में पढ़ता है (डिफ़ॉल्ट रूप से 4 KB)। \n
- पढ़ने के साथ-साथ, यह JSON को टोकन दर टोकन पार्स करता है और संरचना को भरता है। \n
लाभ — आप पूरे JSON को मेमोरी में संग्रहीत नहीं करते हैं। नुकसान — बफ़र प्रबंधन और स्ट्रीम रीडिंग पर थोड़ा अधिक ओवरहेड।
\n\nप्रयोग: बेंचमार्क झूठ नहीं बोलते
\n\nआइए सिद्धांत को व्यवहार में जाँचें। हम तीन परिदृश्यों के लिए एक सरल बेंचमार्क लिखेंगे:
\n- \n
- छोटा JSON (1 KB) \n
- मध्यम JSON (100 KB) \n
- बड़ा JSON (10 MB) \n
package main\n\nimport (\n "bytes"\n "encoding/json"\n "testing"\n)\n\ntype Data struct {\n ID int `json:"id"`\n Name string `json:"name"`\n Items []int `json:"items"`\n}\n\nfunc generateJSON(size int) []byte {\n data := Data{\n ID: 1,\n Name: "test",\n Items: make([]int, size),\n }\n for i := 0; i < size; i++ {\n data.Items[i] = i\n }\n b, _ := json.Marshal(data)\n return b\n}\n\nfunc BenchmarkUnmarshal(b *testing.B) {\n jsonData := generateJSON(1000) // 1 KB\n var result Data\n for i := 0; i < b.N; i++ {\n json.Unmarshal(jsonData, &result)\n }\n}\n\nfunc BenchmarkDecoder(b *testing.B) {\n jsonData := generateJSON(1000) // 1 KB\n var result Data\n for i := 0; i < b.N; i++ {\n reader := bytes.NewReader(jsonData)\n json.NewDecoder(reader).Decode(&result)\n }\n}\n\nपरिणाम (मेरे लैपटॉप पर, Go 1.22):
\n\n