मानचित्र को संरचना में परिवर्तित करना


94

मैं गो में एक जेनेरिक विधि बनाने की कोशिश कर रहा हूं जो ए structसे डेटा का उपयोग करेगा map[string]interface{}। उदाहरण के लिए, विधि हस्ताक्षर और उपयोग की तरह लग सकता है:

func FillStruct(data map[string]interface{}, result interface{}) {
    ...
}

type MyStruct struct {
    Name string
    Age  int64
}

myData := make(map[string]interface{})
myData["Name"] = "Tony"
myData["Age"]  = 23

result := &MyStruct{}
FillStruct(myData, result)

// result now has Name set to "Tony" and Age set to 23

मुझे पता है कि यह एक मध्यस्थ के रूप में JSON का उपयोग करके किया जा सकता है; क्या ऐसा करने का एक और अधिक कुशल तरीका है?


1
JSON का उपयोग एक मध्यस्थ के रूप में, वैसे भी प्रतिबिंब का उपयोग करेगा .. मान लें कि आप encoding/jsonउस मध्यवर्ती चरण को करने के लिए stdlib पैकेज का उपयोग करने जा रहे हैं .. क्या आप एक उदाहरण मानचित्र और उदाहरण संरचना दे सकते हैं जिसे इस पद्धति का उपयोग किया जा सकता है?
साइमन व्हाइटहेड

हाँ, यही कारण है कि मैं JSON से बचने की कोशिश कर रहा हूं। उम्मीद की तरह लगता है कि एक अधिक कुशल विधि है जिसके बारे में मुझे नहीं पता है।
tgrosinger

क्या आप एक उदाहरण उपयोग का मामला दे सकते हैं? जैसा कि - कुछ छद्मकोड दिखाएं जो दर्शाता है कि यह विधि क्या करेगी?
साइमन व्हाइटहेड

हम्म ... unsafeपैकेज के साथ एक तरीका हो सकता है .. लेकिन मैंने कोशिश नहीं की। इसके अलावा .. प्रतिबिंब की आवश्यकता है, क्योंकि आपको इसके गुणों में डेटा रखने के लिए एक प्रकार से जुड़ी मेटाडेटा को क्वेरी करने में सक्षम होना चाहिए। इसे कॉल json.Marshal+ में लपेटने के लिए काफी सीधा होगा json.Decode। लेकिन यह प्रतिबिंब दोगुना है।
शमौन व्हाइटहेड

मैंने प्रतिबिंब के बारे में अपनी टिप्पणी को हटा दिया है। मैं अधिक से अधिक कुशलता से इसे करने में दिलचस्पी रखता हूं। अगर इसका मतलब है कि प्रतिबिंब का उपयोग करना ठीक है।
tgrosinger

जवाबों:


110

सबसे सरल तरीका होगा https://github.com/mitchellh/mapstructure का उपयोग करना

import "github.com/mitchellh/mapstructure"

mapstructure.Decode(myData, &result)

यदि आप इसे स्वयं करना चाहते हैं, तो आप कुछ ऐसा कर सकते हैं:

http://play.golang.org/p/tN8mxT_V9h

func SetField(obj interface{}, name string, value interface{}) error {
    structValue := reflect.ValueOf(obj).Elem()
    structFieldValue := structValue.FieldByName(name)

    if !structFieldValue.IsValid() {
        return fmt.Errorf("No such field: %s in obj", name)
    }

    if !structFieldValue.CanSet() {
        return fmt.Errorf("Cannot set %s field value", name)
    }

    structFieldType := structFieldValue.Type()
    val := reflect.ValueOf(value)
    if structFieldType != val.Type() {
        return errors.New("Provided value type didn't match obj field type")
    }

    structFieldValue.Set(val)
    return nil
}

type MyStruct struct {
    Name string
    Age  int64
}

func (s *MyStruct) FillStruct(m map[string]interface{}) error {
    for k, v := range m {
        err := SetField(s, k, v)
        if err != nil {
            return err
        }
    }
    return nil
}

func main() {
    myData := make(map[string]interface{})
    myData["Name"] = "Tony"
    myData["Age"] = int64(23)

    result := &MyStruct{}
    err := result.FillStruct(myData)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(result)
}

1
धन्यवाद। मैं थोड़ा संशोधित संस्करण का उपयोग कर रहा हूं। play.golang.org/p/_JuMm6HMnU
tgrosinger

मैं अपनी सभी विभिन्न संरचनाओं पर फिलस्ट्रक्ट व्यवहार चाहता हूं और func (s MyStr...) FillStruct ...हर एक के लिए परिभाषित नहीं करना है । क्या आधार संरचना के लिए फिलस्ट्रक्ट को परिभाषित करना संभव है तो क्या मेरे अन्य सभी ढांचे में 'व्यवहार' है? ऊपर दिए गए प्रतिमान में केवल आधार संरचना के बाद से यह संभव नहीं है ... इस मामले में "MyStruct" वास्तव में इसे खेतों में पुनरावृत्त होगा
StartupGuy

मेरा मतलब है कि आप इस तरह से किसी भी संरचना के लिए काम कर सकते हैं: play.golang.org/p/0weG38IUA9
डेव

क्या मिस्ट्रोक्ट में टैग लागू करना संभव है?
vicTROLLA

1
@ अभिषेक निश्चित रूप से एक प्रदर्शन जुर्माना है जिसे आप पहले पाठ और फिर अनमर्शलिंग के लिए भुगतान करेंगे। यह दृष्टिकोण निश्चित रूप से सरल है। यह एक व्यापार है, और आम तौर पर मैं सरल समाधान के साथ जाना होगा। मैंने इस समाधान के साथ उत्तर दिया क्योंकि प्रश्न में कहा गया है "मुझे पता है कि यह JSON का उपयोग मध्यस्थ के रूप में किया जा सकता है, क्या ऐसा करने का एक और अधिक कुशल तरीका है?"। यह समाधान अधिक कुशल होगा, JSON समाधान आमतौर पर लागू करने और इसके बारे में तर्क करने में आसान होगा।
डेव

72

हैशिकॉर्प की https://github.com/mitchellh/mapstructure लाइब्रेरी इसे बॉक्स से बाहर करती है:

import "github.com/mitchellh/mapstructure"

mapstructure.Decode(myData, &result)

दूसरे resultपैरामीटर में संरचना का पता होना चाहिए।


क्या होगा अगर नक्शा कुंजी है user_nameऔर दायर की गई संरचना है UserName?
निकोलस जेला

1
@ निचलोलजैला यह संभाल सकती है कि टैग godoc.org/github.com/mitchellh/mapstructure#ex-Decode--Tags
दीवार पर सर्किट

क्या होगा अगर मैप kye _id है और मैप का नाम Id है तो इसे डिकोड नहीं करेंगे।
रवि शंकर

24
  • encoding/jsonपैकेज का उपयोग करने का सबसे सरल तरीका है

सिर्फ उदाहरण के लिए:

package main
import (
    "fmt"
    "encoding/json"
)

type MyAddress struct {
    House string
    School string
}
type Student struct {
    Id int64
    Name string
    Scores float32
    Address MyAddress
    Labels []string
}

func Test() {

    dict := make(map[string]interface{})
    dict["id"] = 201902181425       // int
    dict["name"] = "jackytse"       // string
    dict["scores"] = 123.456        // float
    dict["address"] = map[string]string{"house":"my house", "school":"my school"}   // map
    dict["labels"] = []string{"aries", "warmhearted", "frank"}      // slice

    jsonbody, err := json.Marshal(dict)
    if err != nil {
        // do error check
        fmt.Println(err)
        return
    }

    student := Student{}
    if err := json.Unmarshal(jsonbody, &student); err != nil {
        // do error check
        fmt.Println(err)
        return
    }

    fmt.Printf("%#v\n", student)
}

func main() {
    Test()
}

1
धन्यवाद @jackytse यह वास्तव में ऐसा करने का सबसे अच्छा तरीका है !! मानचित्र संरचना अक्सर एक इंटरफेस के भीतर नेस्टेड मैप के साथ काम नहीं करती है। इसलिए यह बेहतर है कि एक मैप स्ट्रिंग इंटरफेस पर विचार करें और इसे एक जसन के रूप में संभालें।
गिल्स एस्सोकी

उपरोक्त स्निपेट के लिए खेल का मैदान लिंक पर जाएं: play.golang.org/p/JaKxETAbsnT
जुनैद

13

आप इसे कर सकते हैं ... यह थोड़ा बदसूरत हो सकता है और आपको मैपिंग प्रकारों के संदर्भ में कुछ परीक्षण और त्रुटि का सामना करना पड़ेगा .. लेकिन इसके मूल सार का पता लगाता है:

func FillStruct(data map[string]interface{}, result interface{}) {
    t := reflect.ValueOf(result).Elem()
    for k, v := range data {
        val := t.FieldByName(k)
        val.Set(reflect.ValueOf(v))
    }
}

कार्य नमूना: http://play.golang.org/p/PYHz63sbvL


1
यह शून्य मानों से घबराहट प्रकट करता है:reflect: call of reflect.Value.Set on zero Value
जेम्स टेलर

@JamesTaylor हाँ। मेरा उत्तर मानता है कि आप वास्तव में जानते हैं कि आप किन क्षेत्रों में मैपिंग कर रहे हैं। यदि आप अधिक त्रुटि हैंडलिंग (आप अनुभव कर रहे त्रुटि सहित) के साथ एक समान उत्तर के बाद हैं, तो मैं इसके बजाय Daves उत्तर देना चाहूंगा।
साइमन व्हाइटहेड

2

मैं डेव के उत्तर को अनुकूलित करता हूं, और एक पुनरावर्ती सुविधा जोड़ता हूं। मैं अभी भी अधिक उपयोगकर्ता के अनुकूल संस्करण पर काम कर रहा हूं। उदाहरण के लिए, नक्शे में एक संख्या स्ट्रिंग को संरचना में इंट में परिवर्तित करने में सक्षम होना चाहिए।

package main

import (
    "fmt"
    "reflect"
)

func SetField(obj interface{}, name string, value interface{}) error {

    structValue := reflect.ValueOf(obj).Elem()
    fieldVal := structValue.FieldByName(name)

    if !fieldVal.IsValid() {
        return fmt.Errorf("No such field: %s in obj", name)
    }

    if !fieldVal.CanSet() {
        return fmt.Errorf("Cannot set %s field value", name)
    }

    val := reflect.ValueOf(value)

    if fieldVal.Type() != val.Type() {

        if m,ok := value.(map[string]interface{}); ok {

            // if field value is struct
            if fieldVal.Kind() == reflect.Struct {
                return FillStruct(m, fieldVal.Addr().Interface())
            }

            // if field value is a pointer to struct
            if fieldVal.Kind()==reflect.Ptr && fieldVal.Type().Elem().Kind() == reflect.Struct {
                if fieldVal.IsNil() {
                    fieldVal.Set(reflect.New(fieldVal.Type().Elem()))
                }
                // fmt.Printf("recursive: %v %v\n", m,fieldVal.Interface())
                return FillStruct(m, fieldVal.Interface())
            }

        }

        return fmt.Errorf("Provided value type didn't match obj field type")
    }

    fieldVal.Set(val)
    return nil

}

func FillStruct(m map[string]interface{}, s interface{}) error {
    for k, v := range m {
        err := SetField(s, k, v)
        if err != nil {
            return err
        }
    }
    return nil
}

type OtherStruct struct {
    Name string
    Age  int64
}


type MyStruct struct {
    Name string
    Age  int64
    OtherStruct *OtherStruct
}



func main() {
    myData := make(map[string]interface{})
    myData["Name"]        = "Tony"
    myData["Age"]         = int64(23)
    OtherStruct := make(map[string]interface{})
    myData["OtherStruct"] = OtherStruct
    OtherStruct["Name"]   = "roxma"
    OtherStruct["Age"]    = int64(23)

    result := &MyStruct{}
    err := FillStruct(myData,result)
    fmt.Println(err)
    fmt.Printf("%v %v\n",result,result.OtherStruct)
}

0

दो चरण हैं:

  1. JSON बाइट में इंटरफ़ेस बदलें
  2. JSON बाइट को संरचना में बदलें

नीचे एक उदाहरण है:

dbByte, _ := json.Marshal(dbContent)
_ = json.Unmarshal(dbByte, &MyStruct)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.