Home > front end >  Is there an easy way to create a struct in golang?
Is there an easy way to create a struct in golang?

Time:06-21

I have a struct and now want to instantiate it from received http data. But now the code I write is cumbersome and has a lot of lines of code. Is there any way to simplify the code? All fields except the field id can correspond

model

type ALiNotifyLog struct {
    ID             *int    `json:"id"`
    APPId          *string `json:"app_id"`
    AuthAppId      *string `json:"auth_app_id"`
    BuyerId        *string `json:"buyer_id"`
    BuyerPayAmount *string `json:"buyer_pay_amount"`
    GmtCreate      *string `json:"gmt_create"`
    GmtPayment     *string `json:"gmt_payment"`
    InvoiceAmount  *string `json:"invoice_amount"`
    NotifyId       *string `json:"notify_id"`
    NotifyTime     *string `json:"notify_time"`
    OutTradeNo     *string `json:"out_trade_no"`
    PointAmount    *string `json:"point_amount"`
    ReceiptAmount  *string `json:"receipt_amount"`
    Sign           *string `json:"sign"`
    TotalAmount    *string `json:"total_amount"`
    TradeNo        *string `json:"trade_no"`
    TradeStatus    *string `json:"trade_status"`
}

func

func SaveData(data map[string]interface{}) {
    app_id := data["app_id"].(string)
    auth_app_id := data["auth_app_id"].(string)
    buyer_id := data["buyer_id"].(string)
    buyer_pay_amount := data["buyer_pay_amount"].(string)
    gmt_create := data["gmt_create"].(string)
    gmt_payment := data["gmt_payment"].(string)
    invoice_amount := data["invoice_amount"].(string)
    notify_id := data["notify_id"].(string)
    notify_time := data["notify_time"].(string)
    out_trade_no := data["out_trade_no"].(string)
    point_amount := data["point_amount"].(string)
    receipt_amount := data["receipt_amount"].(string)
    sign := data["sign"].(string)
    total_amount := data["total_amount"].(string)
    trade_no := data["trade_no"].(string)
    trade_status := data["trade_status"].(string)
    
    model := payment.ALiNotifyLog{
        APPId:          &app_id,
        AuthAppId:      &auth_app_id,
        BuyerId:        &buyer_id,
        BuyerPayAmount: &buyer_pay_amount,
        GmtCreate:      &gmt_create,
        GmtPayment:     &gmt_payment,
        InvoiceAmount:  &invoice_amount,
        NotifyId:       &notify_id,
        NotifyTime:     &notify_time,
        OutTradeNo:     &out_trade_no,
        PointAmount:    &point_amount,
        ReceiptAmount:  &receipt_amount,
        Sign:           &sign,
        TotalAmount:    &total_amount,
        TradeNo:        &trade_no,
        TradeStatus:    &trade_status}
    res := global.Orm.Table(paynotifylog).Create(&model)
    fmt.Println(res)
}

CodePudding user response:

I'd say converting the map to json then unmarshalling it would be the easiest way, not very efficient though.

https://go.dev/play/p/zxgupUCPpe2


func GhettoConvert[E any](in map[string]any) (out E) {
    j, _ := json.Marshal(in) // should check the error
    _ = json.Unmarshal(j, &out)
    return
}

There was a library that used reflection to do that directly, but for the life of me I can't find it.

CodePudding user response:

I see that you are JSON. May be decode the JSON directly into a struct instance.

I will structure the code something like the below snippet:

type ALiNotifyLog struct {
    // your fields here
}

func parseRequest(r *http.Request) {
    var notifyLog ALiNotifyLog
    err := json.NewDecoder(r.Body).Decode(&notifyLog)
    if err != nil {
        // do something
    }

    // ............ more code

}

func SaveData(data ALiNotifyLog) {
    res := global.Orm.Table(paynotifylog).Create(&data)
    fmt.Println(res)

    // ........... more code
}

CodePudding user response:

Use the reflect package to programmatically iterate over the fields:

func setStringpFields(pmodel any, data map[string]any) {
    v := reflect.ValueOf(pmodel).Elem()
    t := v.Type()
    for i := 0; i < t.NumField(); i   {
        sf := t.Field(i)
        if sf.Type != stringpType {
            continue
        }
        name, _, _ := strings.Cut(sf.Tag.Get("json"), ",")
        if s, ok := data[name].(string); ok {
            v.Field(i).Set(reflect.ValueOf(&s))
        }
    }
}

var stringpType = reflect.PtrTo(reflect.TypeOf(""))

Use it like this:

var model ALiNotifyLog
setStringpFields(&model, data)

Run an example on the Go Playground.

I took the liberty of skipping fields that are missing from data. The code in the question panics on a missing value.

A simpler approach is to create a function with the repeated functionality:

func stringp(data map[string]interface{}, name string) *string {
    if s, ok := data[name].(string); ok {
        return &s
    }
    return nil
}

Use that function to initialize the fields:

model := payment.ALiNotifyLog{
    APPId:          stringp("app_id"),
    AuthAppId:      stringp("auth_app_id"),
    ...
    TradeStatus:    stringp("trade_status")}
res := global.Orm.Table(paynotifylog).Create(&model)
fmt.Println(res)

CodePudding user response:

1: ScanMapToStruct

func scanMapToStruct(dest interface{}, vals map[string]interface{}) error {
    srcValue := indirect(reflect.ValueOf(dest))
    srcType := indirectType(reflect.TypeOf(dest))

    for m := 0; m < srcType.NumField(); m   {
        field := srcType.Field(m)
        fieldName, _ := getFieldName(field)
        jsonTypeName := getJsonDataType(field.Type)
        if jsonTypeName == "" {
            continue
        }
        v, ok := vals[fieldName].(string)
        if !ok {
            continue
        }
        dec := decoders[field.Type.Kind()]
        if dec == nil {
            continue
        }
        err := dec(srcValue.Field(m), v)
        if err != nil {
            fmt.Printf("set field(%s)=%s err:%v\n", fieldName, v, err)
            continue
        }
    }
    return nil
  }

2: copier.Copy()

  •  Tags:  
  • go
  • Related