我对 Go 还很陌生,正在尝试确定是否有更简洁的方法来完成从前端 (JS) 到我的 API 的 JSON 数据的规范化。为了确保在从结构 (model.Expense) 创建变量时使用正确的类型,我将有效负载转储到映射中,然后规范化并保存回结构。如果有人可以教我更好的方法来处理这个问题,我将不胜感激!预先感谢!
模型.费用结构:
type Expense struct {
Id primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
Name string `json:"name"`
Frequency int `json:"frequency"`
StartDate *time.Time `json:"startDate"`
EndDate *time.Time `json:"endDate,omitempty"`
Cost primitive.Decimal128 `json:"cost"`
Paid []string `json:"paid,omitempty"`
}
有问题的控制器:
func InsertOneExpense(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Allow-Control-Allow-Methods", "POST")
var expense map[string]interface{}
json.NewDecoder(r.Body).Decode(&expense)
var expenseName string
if name, ok := expense["name"]; ok {
expenseName = fmt.Sprintf("%v", name)
} else {
json.NewEncoder(w).Encode("missing required name")
}
var expenseFrequency int
if frequency, ok := expense["frequency"]; ok {
expenseFrequency = int(frequency.(float64))
} else {
expenseFrequency = 1
}
// Handle startDate normalization
var expenseStartDate *time.Time
if startDate, ok := expense["startDate"]; ok {
startDateString := fmt.Sprintf("%v", startDate)
startDateParsed, err := time.Parse("2006-01-02 15:04:05", startDateString)
if err != nil {
log.Fatal(err)
}
expenseStartDate = &startDateParsed
} else {
json.NewEncoder(w).Encode("missing required startDate")
}
// Handle endDate normalization
var expenseEndDate *time.Time
if endDate, ok := expense["endDate"]; ok {
endDateString := fmt.Sprintf("%v", endDate)
endDateParsed, err := time.Parse("2006-01-02 15:04:05", endDateString)
if err != nil {
log.Fatal(err)
}
expenseEndDate = &endDateParsed
} else {
expenseEndDate = nil
}
// Handle cost normaliztion
var expenseCost primitive.Decimal128
if cost, ok := expense["cost"]; ok {
costString := fmt.Sprintf("%v", cost)
costPrimitive, err := primitive.ParseDecimal128(costString)
if err != nil {
log.Fatal(err)
}
expenseCost = costPrimitive
} else {
json.NewEncoder(w).Encode("missing required cost")
return
}
normalizedExpense := model.Expense{
Name: expenseName,
Frequency: expenseFrequency,
StartDate: expenseStartDate,
EndDate: expenseEndDate,
Cost: expenseCost,
}
// Do more things with the struct var...
}
您可以定义
json.UnmarshalJSON
接口,然后根据需要手动验证数据。 尝试这样的事情:
package main
import (
"encoding/json"
"fmt"
"strconv"
)
type CoolStruct struct {
MoneyOwed string `json:"money_owed"`
}
// UnmarshalJSON the json package will delegate deserialization to our code if we implement the json.UnmarshalJSON interface
func (c *CoolStruct) UnmarshalJSON(data []byte) error {
// get the body as a map[string]*[]byte
raw := map[string]*json.RawMessage{}
if err := json.Unmarshal(data, &raw); err != nil {
return fmt.Errorf("unable to unmarshal raw meessage map: %w", err)
}
// if we don't know the variable type sent we can unmarshal to an interface
var tempHolder interface{}
err := json.Unmarshal(*raw["money_owed"], &tempHolder)
if err != nil {
return fmt.Errorf("unable to unmarshal custom value from raw message map: %w", err)
}
// the unmarshalled interface has an underlying type use go's typing
// system to determine type conversions / normalizations required
switch tempHolder.(type) {
case int64:
// once we determine the type of the we just assign the value
// to the receiver's field
c.MoneyOwed = strconv.FormatInt(tempHolder.(int64), 10)
// we could list all individually or as a group; driven by requirements
case int, int32, float32, float64:
c.MoneyOwed = fmt.Sprint(tempHolder)
case string:
c.MoneyOwed = tempHolder.(string)
default:
fmt.Printf("missing type case: %T\n", tempHolder)
}
// success; struct is now populated
return nil
}
func main() {
myJson := []byte(`{"money_owed": 123.12}`)
cool := CoolStruct{}
// outside of your struct you marshal/unmarshal as normal
if err := json.Unmarshal(myJson, &cool); err != nil {
panic(err)
}
fmt.Printf("%+v\n", cool)
}
输出:
{MoneyOwed:123.12}