如何在Go中将JSON数据规范化为API的结构体?

huangapple go评论83阅读模式
英文:

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

huangapple
  • 本文由 发表于 2023年5月9日 19:48:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76208994.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定