【问题标题】:Creating structs programmatically at runtime - possible?在运行时以编程方式创建结构 - 可能吗?
【发布时间】:2019-05-01 09:31:08
【问题描述】:

是否可以在 Go 中以编程方式创建结构类型(即不在编译的源代码中)?

我们有一个特殊的用例,其中将通过用户定义的元数据创建类型(因此事先不知道架构/类型) 并且会因每个客户而异。然后,我们需要为这些服务自动生成 REST 服务,并将它们保存在 NoSQL 后端。 我们还需要为每个字段定义不同的动态验证器(例如强制、正则表达式、最大/最小大小、最大/最小值、对另一个类型实例的引用等)

我想知道在 Go 中是否有类似的可能?

编辑 1:

例如

来自 JSON 中的前端

For customer 1:
{
"id":1,
"patientid":1,
"name":"test1",
"height":"160",
"weight":"70",
"temp":"98"
}

For customer 2:
{
"id":2,
"patientid":1,
"name":"test1",
"height":"160",
"weight":"70"
}

For customer 3

may be different new fields will add

后台

// For One customer need to have these fields 

type Vitalsigns struct {
    ID                int64  `datastore:"-"`
    PatientID         int64  `json:"patientid,omitempty"`
    Name              string `json:"name,omitempty"`
    Height            string `json:"height,omitempty"`
    Weight            string `json:"weight,omitempty"`
    Temp              string `json:"temp,omitempty"`
}



// Another need to have these fields

type Vitalsigns struct {
    ID                int64  `datastore:"-"`
    PatientID         int64  `json:"patientid,omitempty"`
    Name              string `json:"name,omitempty"`
    Height            string `json:"height,omitempty"`
    Weight            string `json:"weight,omitempty"`
}


//CreateVitalsignsHandler is to create vitals for a patient
func CreateVitalsignsHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {

    //Creating the Vitalsigns
    kinVitalsigns := &Vitalsigns{}
    ctx := appengine.NewContext(r)
    if err := json.NewDecoder(r.Body).Decode(kinVitalsigns); err != nil {
        RespondErr(w, r, http.StatusInternalServerError, err.Error())
        return
    }
    //Getting namespace
    namespace := ps.ByName("namespace")
    ctx, err := appengine.Namespace(ctx, namespace)
    if err != nil {
        log.Infof(ctx, "Namespace error from CreateVitalsignsHandler")
        RespondErr(w, r, http.StatusInternalServerError, err.Error())
        return
    }
    //Geting the patientID
    pID, err := strconv.Atoi(ps.ByName("id"))
    if err != nil {
        log.Infof(ctx, "Srting to Int64 conversion error from CreateVitalsignsHandler")
        RespondErr(w, r, http.StatusInternalServerError, err.Error())
        return
    }
    patientID := int64(pID)
    kinVitalsigns.PatientID = patientID

    //Generating the key
    vitalsignsKey := datastore.NewIncompleteKey(ctx, "Vitalsigns", nil)

    //Inserting the data to the datastore
    vk, err := datastore.Put(ctx, vitalsignsKey, kinVitalsigns)
    if err != nil {
        log.Infof(ctx, "Entity creation was failed from CreateVitalsignsHandler")
        RespondErr(w, r, http.StatusInternalServerError, err.Error())
        return
    }
    kinVitalsigns.ID = vk.IntID()
    message := "Vitalsigns created successfully!! "
    Respond(w, r, http.StatusOK, SuccessResponse{kinVitalsigns.ID, 0, "", message})
    return
}

【问题讨论】:

    标签: go struct reflection types


    【解决方案1】:

    编辑:您的编辑表明您希望处理动态对象以从 Google 数据存储中放置/检索。为此,完全没有必要在运行时创建结构类型,您可以只使用此答案中提供的动态映射:How can I have dynamic properties in go on the google app engine datastore

    原始答案如下。


    请注意,如果类型在编译时已知,最好/最有效的是创建类型并编译它们,所以一切都是“静态的”。您可以手动创建类型,也可以使用go generate 自动执行该过程。

    另请注意,您可能不一定需要结构类型来建模动态对象,很多时候映射可能就足够了。

    如果在编译时类型未知,而结构类型是必须的,请继续阅读。

    是的,可以在运行时使用 Go 的反射创建“动态”结构类型,特别是使用 reflect.StructOf() 函数。

    让我们看一个简单的例子,在运行时创建一个结构类型,它有一个Name string 和一个Age int 字段:

    t := reflect.StructOf([]reflect.StructField{
        {
            Name: "Name",
            Type: reflect.TypeOf(""), // string
        },
        {
            Name: "Age",
            Type: reflect.TypeOf(0), // int
        },
    })
    
    fmt.Println(t)
    
    v := reflect.New(t)
    fmt.Printf("%+v\n", v)
    v.Elem().FieldByName("Name").Set(reflect.ValueOf("Bob"))
    v.Elem().FieldByName("Age").Set(reflect.ValueOf(12))
    
    fmt.Printf("%+v\n", v)
    

    这个输出(在Go Playground上试试):

    struct { Name string; Age int }
    &{Name: Age:0}
    &{Name:Bob Age:12}
    

    如果您想定义验证规则,您可以为此使用第 3 方库,例如 github.com/go-validator/validator。这个包使用struct tags来指定验证规则,你也可以使用反射指定结构标签。

    例如,如果要指定Name必须至少3个字符,最多40个字符,并且只能包含英文字母,Age的有效范围为6..100(两者都包含),这就是它的样子:

    t := reflect.StructOf([]reflect.StructField{
        {
            Name: "Name",
            Type: reflect.TypeOf(""), // string
            Tag:  reflect.StructTag(`validate:"min=3,max=40,regexp=^[a-zA-Z]*$"`),
        },
        {
            Name: "Age",
            Type: reflect.TypeOf(0), // int
            Tag:  reflect.StructTag(`validate:"min=6,max=100"`),
        },
    })
    

    打印此类型将输出(由我包装)(在Go Playground 上尝试):

    struct { Name string "validate:\"min=3,max=40,regexp=^[a-zA-Z]*$\"";
        Age int "validate:\"min=6,max=100\"" }
    

    创建此结构的实例后,您可以使用validator.Validate() 函数对其进行验证,例如:

    v := reflect.New(t)
    v.Elem().FieldByName("Name").Set(reflect.ValueOf("Bob"))
    v.Elem().FieldByName("Age").Set(reflect.ValueOf(12))
    
    if errs := validator.Validate(v.Elem().Interface()); errs != nil {
        // values not valid, deal with errors here
    }
    

    【讨论】:

    • 我已经在上面的代码中更新了我的答案有点混乱使用我将从前端获取动态 JSON,如何转换为结构以将数据插入数据存储区。
    • 虽然这是对已发布问题的一个很好的解决方案,但我强烈反对使用它。这似乎是一场噩梦,运行缓慢,最终很可能毫无意义。编译时结构或映射几乎肯定是更好的解决方案。
    • @Adrian 任何建议
    • @Adrian 是的,这正是我在第一段中的建议,“剩下的”是回答这个问题。
    • 是的,只是第二次投票支持“真的,不要这样做,即使它是可能的”!
    猜你喜欢
    • 2010-11-09
    • 1970-01-01
    • 2015-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-21
    • 2016-03-25
    • 2015-10-31
    相关资源
    最近更新 更多