douzhao1912 2019-05-01 09:31
浏览 13
已采纳

在运行时以编程方式创建结构-可能吗?

Is it possible in Go to create a struct type programmatically (i.e. not in the compiled source code)?

We have a particular use case where a type will be created via user-defined metadata (so the schema/types are not known in advance) and will vary for every customer. We would then need to auto-generate REST services for those and persist them in a NoSQL backend. We would also need to define different dynamic validators per field (e.g. mandatory, regex, max/min size, max/min value, a reference to another type instance, etc.)

I was wondering if something similar is possible in the Go?

Edit 1:

For example

From frontend in 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

Backend

// 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
}
  • 写回答

1条回答 默认 最新

  • dream2891 2019-05-01 09:45
    关注

    Edit: Your edit reveals you want to handle dynamic objects to put / retrieve from Google Datastore. For this it is completely unnecessary to create struct types at runtime, you may just use a dynamic map presented in this answer: How can I have dynamic properties in go on the google app engine datastore.

    Original answer follows.


    Note that if the types are known at compile time, best / most efficient is to create the types and compile them, so everything will be "static". You may create the types manually, or you may use go generate to automate the process.

    Also note that you may not necessarily need struct types to model dynamic objects, many times maps may be sufficient.

    If types are not known at compile time, and struct types are a must, read on.

    Yes, it's possible to create "dynamic" struct types at runtime using Go's reflection, specifically with the reflect.StructOf() function.

    Let's see a simple example, creating a struct type at runtime that has a Name string and an Age int field:

    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
    ", v)
    v.Elem().FieldByName("Name").Set(reflect.ValueOf("Bob"))
    v.Elem().FieldByName("Age").Set(reflect.ValueOf(12))
    
    fmt.Printf("%+v
    ", v)
    

    This outputs (try it on the Go Playground):

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

    If you want to define validation rules, you may use a 3rd party lib for that, for example github.com/go-validator/validator. This package uses struct tags to specify validation rules, struct tags which you may also specify using reflection.

    For example, if you want to specify that the Name must be at least 3 characters and 40 at most, and it may only contain letters of the English alphabet, and valid range for Age is 6..100 (both inclusive), this is how it would look like:

    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"`),
        },
    })
    

    Printing this type would output (wrapped by me) (try it on the Go Playground):

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

    Once you create an instance of this struct, you can validate it using the validator.Validate() function, e.g.:

    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
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 (标签-STM32|关键词-智能小车)
  • ¥20 关于#stm32#的问题,请各位专家解答!
  • ¥15 (标签-python)
  • ¥15 第一个已完成,求第二个做法
  • ¥20 搭建awx,试了很多版本都有错
  • ¥15 java corba的客户端该如何指定使用本地某个固定IP去连接服务端?
  • ¥15 activiti工作流问题,求解答
  • ¥15 有人写过RPA后台管理系统么?
  • ¥15 Bioage计算生物学年龄
  • ¥20 如何将FPGA Alveo U50恢复原来出厂设置哇?