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 蓝桥oj3931,请问我错在哪里
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看
  • ¥15 关于#Java#的问题,如何解决?
  • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
  • ¥15 cmd cl 0x000007b
  • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line
  • ¥500 火焰左右视图、视差(基于双目相机)
  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染