Создание структур программно во время выполнения - возможно? - PullRequest
1 голос
/ 01 мая 2019

Возможно ли в Go создать тип структуры программно (т.е. не в скомпилированном исходном коде)?

У нас есть особый вариант использования, когда тип будет создаваться с помощью пользовательских метаданных (поэтому схема / типы заранее неизвестны) и будет варьироваться для каждого клиента. Затем нам потребуется автоматически сгенерировать REST-сервисы для них и сохранить их в бэкэнде NoSQL. Нам также необходимо определить разные динамические валидаторы для каждого поля (например, обязательное, регулярное выражение, максимальный / минимальный размер, максимальное / минимальное значение, ссылка на экземпляр другого типа и т. Д.)

Мне было интересно, возможно ли что-то подобное в Го?

Редактировать 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

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 Ответ

8 голосов
/ 01 мая 2019

Редактировать: Ваше редактирование показывает, что вы хотите обрабатывать динамические объекты, которые нужно вставить / извлечь из Google Datastore.Для этого совершенно необязательно создавать типы структур во время выполнения, вы можете просто использовать динамическую карту, представленную в этом ответе: Как получить динамические свойства в хранилище данных движка приложений Google .

Ниже следует оригинальный ответ.


Обратите внимание, что если типы известны во время компиляции, лучше всего / эффективнее создавать типы и компилировать их, поэтому все будет "статичным".Вы можете создавать типы вручную или использовать 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}

Если вы хотите определить правила проверки, вы можете использовать стороннюю библиотеку для этого, дляпример github.com/go-validator/validator.Этот пакет использует теги struct для указания правил проверки, теги struct, которые вы также можете указать с помощью отражения.

Например, если вы хотите указать, что 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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...