英文:
How do I best normalize JSON data to API in Go for a struct
问题
我对Go语言还不太熟悉,但是我可以帮你翻译这段代码。以下是翻译好的内容:
我对Go语言还不太熟悉,正在尝试确定是否有更简洁的方法来实现从前端(JS)到我的API的JSON数据的规范化。为了确保在从结构体(model.Expense)创建变量时使用正确的类型,我将有效载荷转储到一个映射中,然后进行规范化,并保存回结构体。如果有人能告诉我更好的处理方法,我将非常感激!提前谢谢!
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
}
// 处理startDate的规范化
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")
}
// 处理endDate的规范化
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
}
// 处理cost的规范化
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,
}
// 对结构体变量进行更多操作...
}
英文:
I am quite new to Go and am trying to identify if there is a more succinct way to accomplish normalization of JSON data coming from the front end (JS) to my API. In order to ensure that I am using the correct types when creating a variable from my struct (model.Expense), I am dumping the payload into a map, then normalizing, and saving back to a struct. If someone could school me on a better way of handling this, I would greatly appreciate it! Thanks in advance!
model.Expense struct:
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"`
}
Controller in question:
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...
}
答案1
得分: 1
你可以定义json.UnmarshalJSON
接口,然后根据需要手动验证数据。尝试像这样做:
package main
import (
"encoding/json"
"fmt"
"strconv"
)
type CoolStruct struct {
MoneyOwed string `json:"money_owed"`
}
// 通过实现json.UnmarshalJSON接口,json包将会将反序列化的工作委托给我们的代码
func (c *CoolStruct) UnmarshalJSON(data []byte) error {
// 将body解析为map[string]*[]byte
raw := map[string]*json.RawMessage{}
if err := json.Unmarshal(data, &raw); err != nil {
return fmt.Errorf("无法解析原始消息映射:%w", err)
}
// 如果我们不知道发送的变量类型,可以将其解析为一个接口
var tempHolder interface{}
err := json.Unmarshal(*raw["money_owed"], &tempHolder)
if err != nil {
return fmt.Errorf("无法从原始消息映射中解析自定义值:%w", err)
}
// 解析后的接口有一个底层类型,使用Go的类型系统来确定所需的类型转换/规范化
switch tempHolder.(type) {
case int64:
// 一旦确定了类型,我们只需将值分配给接收器的字段
c.MoneyOwed = strconv.FormatInt(tempHolder.(int64), 10)
// 可以逐个列出所有类型,也可以作为一个组列出,根据需求而定
case int, int32, float32, float64:
c.MoneyOwed = fmt.Sprint(tempHolder)
case string:
c.MoneyOwed = tempHolder.(string)
default:
fmt.Printf("缺少类型情况:%T\n", tempHolder)
}
// 成功;结构体现在已填充
return nil
}
func main() {
myJson := []byte(`{"money_owed": 123.12}`)
cool := CoolStruct{}
// 在结构体之外,可以像平常一样进行编组/解组
if err := json.Unmarshal(myJson, &cool); err != nil {
panic(err)
}
fmt.Printf("%+v\n", cool)
}
输出:{MoneyOwed:123.12}
Playground链接:https://go.dev/play/p/glStUbwpCCX
英文:
You can define the json.UnmarshalJSON
interface and then manually validate data however you need. Try something like this:
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)
}
Output: {MoneyOwed:123.12}
Playground link: https://go.dev/play/p/glStUbwpCCX
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论