गो के साथ JSON में एक खाली संरचना को मार्शल कैसे नहीं करें?


88

मेरे पास इस तरह की एक संरचना है:

type Result struct {
    Data       MyStruct  `json:"data,omitempty"`
    Status     string    `json:"status,omitempty"`
    Reason     string    `json:"reason,omitempty"`
}

लेकिन यहां तक ​​कि अगर MyStruct का उदाहरण पूरी तरह से खाली है (मतलब, सभी मान डिफ़ॉल्ट हैं), इसे इस रूप में क्रमबद्ध किया जा रहा है:

"data":{}

मुझे पता है कि एन्कोडिंग / json डॉक्स निर्दिष्ट करते हैं कि "खाली" फ़ील्ड हैं:

झूठा, 0, किसी भी शून्य सूचक या इंटरफ़ेस मूल्य, और किसी भी सरणी, टुकड़ा, नक्शा या स्ट्रिंग की लंबाई शून्य

लेकिन सभी खाली / डिफ़ॉल्ट मूल्यों के साथ एक संरचना के लिए कोई विचार नहीं है। इसके सभी क्षेत्रों को भी टैग किया गया है omitempty, लेकिन इसका कोई प्रभाव नहीं है।

मैं JSON पैकेज कैसे प्राप्त कर सकता हूं कि मेरे क्षेत्र को खाली न करें जो कि एक खाली संरचना है?

जवाबों:


137

जैसा कि डॉक्स कहते हैं, "कोई भी शून्य सूचक।" - स्ट्रक्चर को पॉइंटर बनाएं। संकेत स्पष्ट "खाली" मान हैं nil:।

फिक्स - एक प्रकार का पॉइंटर पॉइंटर फ़ील्ड के साथ परिभाषित करें :

type Result struct {
    Data       *MyStruct `json:"data,omitempty"`
    Status     string    `json:"status,omitempty"`
    Reason     string    `json:"reason,omitempty"`
}

फिर इस तरह एक मूल्य:

result := Result{}

के रूप में मार्शल करेंगे:

{}

स्पष्टीकरण: *MyStructहमारे प्रकार की परिभाषा में देखें। JSON क्रमांकन परवाह नहीं करता है कि यह एक सूचक है या नहीं - यह एक रनटाइम विवरण है। तो पॉइंटर्स में स्ट्रक्चर फील्ड बनाने से केवल संकलन और क्रम के निहितार्थ होते हैं)।

बस ध्यान दें कि अगर आप से क्षेत्र प्रकार बदल कर MyStructकरने के लिए *MyStruct, आप इसे पॉप्युलेट करने के लिए है, इसलिए की तरह struct मूल्यों के संकेत की आवश्यकता होगी:

Data: &MyStruct{ /* values */ }

2
आशीर्वाद आप मैट, यह वही है जिसकी मुझे तलाश थी
वेंकट एसएसकेएम चैतन्य

@ मैट, क्या आप सुनिश्चित हैं कि &MyStruct{ /* values */ }एक शून्य सूचक के रूप में गिना जाता है? मान शून्य नहीं है।
शुझेंग

@Matt क्या यह डिफ़ॉल्ट व्यवहार करना संभव है? मैं हमेशा छोड़ देना चाहता हूं। (मूल रूप से सभी संरचनाओं के प्रत्येक क्षेत्र में टैग का उपयोग नहीं करें)
मोहित सिंह

17

@Chakrit एक टिप्पणी में उल्लेख किया है, आप नहीं इस काम के लिए लागू करने से प्राप्त कर सकते हैं json.Marshalerपर MyStruct, और हर struct पर एक कस्टम JSON मार्शलिंग समारोह को लागू करने कि का उपयोग करता है यह एक बहुत अधिक काम हो सकता है। यह वास्तव में आपके उपयोग के मामले पर निर्भर करता है कि क्या यह अतिरिक्त काम के लायक है या आप अपने JSON में खाली संरचना के साथ रहने के लिए तैयार हैं या नहीं, लेकिन यहां मैं जिस पैटर्न का उपयोग करता हूं वह है Result:

type Result struct {
    Data       MyStruct
    Status     string   
    Reason     string    
}

func (r Result) MarshalJSON() ([]byte, error) {
    return json.Marshal(struct {
        Data     *MyStruct   `json:"data,omitempty"`
        Status   string      `json:"status,omitempty"`
        Reason   string      `json:"reason,omitempty"`
    }{
        Data:   &r.Data,
        Status: r.Status,
        Reason: r.Reason,
    })        
}

func (r *Result) UnmarshalJSON(b []byte) error {
    decoded := new(struct {
        Data     *MyStruct   `json:"data,omitempty"`
        Status   string      `json:"status,omitempty"`
        Reason   string      `json:"reason,omitempty"`
    })
    err := json.Unmarshal(b, decoded)
    if err == nil {
        r.Data = decoded.Data
        r.Status = decoded.Status
        r.Reason = decoded.Reason
    }
    return err
}

यदि आपके पास कई क्षेत्रों के साथ विशाल संरचनाएं हैं, तो यह थकाऊ हो सकता है, विशेष रूप से बाद में एक संरचना के कार्यान्वयन को बदल सकता है, लेकिन jsonआपकी आवश्यकताओं के अनुरूप पूरे पैकेज को फिर से लिखना कम नहीं (एक अच्छा विचार नहीं), यह बहुत ही एकमात्र तरीका है जिसे मैं प्राप्त करने के बारे में सोच सकता हूं ऐसा तब भी किया जाता है जब वह एक नॉन-पॉइंटर MyStructको वहां रखता है ।

इसके अलावा, आपको इनलाइन स्ट्रक्चर्स का उपयोग करने की आवश्यकता नहीं है, आप नामित नाम बना सकते हैं। मैं कोड पूरा होने के साथ लाइट का उपयोग करता हूं, इसलिए अव्यवस्था से बचने के लिए मैं इनलाइन पसंद करता हूं।


9

Dataएक आरंभिक संरचना है, इसलिए इसे खाली नहीं माना जाता है क्योंकि यह encoding/jsonकेवल तात्कालिक मूल्य को देखता है, संरचना के अंदर के क्षेत्रों को नहीं।

दुर्भाग्य nilसे json.Marhslerवर्तमान में काम नहीं कर रहा है:

func (_ MyStruct) MarshalJSON() ([]byte, error) {
    if empty {
        return nil, nil // unexpected end of JSON input
    }
    // ...
}

आप Resultएक मार्शल भी दे सकते हैं , लेकिन यह प्रयास के लायक नहीं है।

एकमात्र विकल्प, जैसा कि मैट का सुझाव है, Dataएक पॉइंटर बनाना और मूल्य निर्धारित करना है nil


1
मैं यह नहीं देखता कि संरचना के बाल क्षेत्रों की जांच क्यों encoding/json नहीं कर सकता । यह बहुत कुशल नहीं होगा, हाँ। लेकिन यह निश्चित रूप से असंभव नहीं है।
नेमो

@ नीमो मैं आपकी बात देखता हूं, मैंने शब्द बदल दिया। यह ऐसा नहीं करता क्योंकि यह कुशल नहीं होगा। हालांकि यह json.Marshalerकेस-बाय-केस आधार पर किया जा सकता है ।
ल्यूक

2
यह है नहीं करने के लिए संभव नहीं मौसम का फैसला या MyStructरिक्त है एक को लागू करने से json.Marshalerपर MyStructही। प्रमाण: play.golang.org/p/UEC8A3JGvx
chakrit

ऐसा करने के लिए आपको खुद json.Marshalerको युक्त Resultप्रकार पर लागू करना होगा जो बहुत असुविधाजनक हो सकता है।
चक्र 12

3

इस विशेषता के लिए एक उत्कृष्ट गोलांग प्रस्ताव है जो 4 वर्षों से अधिक सक्रिय है, इसलिए इस बिंदु पर, यह मान लेना सुरक्षित है कि यह जल्द ही मानक पुस्तकालय में नहीं आएगा। जैसा कि @Matt ने बताया है, पारंपरिक दृष्टिकोण को संरक्षकों को पॉइंटर्स-टू-स्ट्रक्चर में बदलना है । यदि यह दृष्टिकोण अचूक (या अव्यवहारिक) है, तो एक वैकल्पिक वैकल्पिक जेन्स एनकोडर का उपयोग करना है जो शून्य मान संरचना को छोड़ने का समर्थन करता है ।

मैंने टैग लगाते समय शून्य मान स्ट्रक्चर्स को छोड़ने के लिए अतिरिक्त समर्थन के साथ गोलंग जोंस लाइब्रेरी ( क्लैक्ट्रम / json ) का दर्पण बनाया omitempty। इस पुस्तकालय पहचान लेता है zeroness लोकप्रिय YAML एनकोडर के लिए एक समान तरीके से जाने के YAML द्वारा रिकर्सिवली जाँच सार्वजनिक struct क्षेत्रों

जैसे

$ go get -u "github.com/clarketm/json"
import (
    "fmt"
    "github.com/clarketm/json" // drop-in replacement for `encoding/json`
)

type Result struct {
    Data   MyStruct `json:"data,omitempty"`
    Status string   `json:"status,omitempty"`
    Reason string   `json:"reason,omitempty"`
}

j, _ := json.Marshal(&Result{
    Status: "204",
    Reason: "No Content",
})

fmt.Println(string(j))
// Note: `data` is omitted from the resultant json.
{
  "status": "204"
  "reason": "No Content"
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.