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

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

How do I best normalize JSON data to API in Go for a struct

问题

我对Go语言还不太熟悉,但是我可以帮你翻译这段代码。以下是翻译好的内容:

我对Go语言还不太熟悉,正在尝试确定是否有更简洁的方法来实现从前端(JS)到我的API的JSON数据的规范化。为了确保在从结构体(model.Expense)创建变量时使用正确的类型,我将有效载荷转储到一个映射中,然后进行规范化,并保存回结构体。如果有人能告诉我更好的处理方法,我将非常感激!提前谢谢!

model.Expense结构体:

  1. type Expense struct {
  2. Id primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
  3. Name string `json:"name"`
  4. Frequency int `json:"frequency"`
  5. StartDate *time.Time `json:"startDate"`
  6. EndDate *time.Time `json:"endDate,omitempty"`
  7. Cost primitive.Decimal128 `json:"cost"`
  8. Paid []string `json:"paid,omitempty"`
  9. }

相关的控制器:

  1. func InsertOneExpense(w http.ResponseWriter, r *http.Request) {
  2. w.Header().Set("Content-Type", "application/json")
  3. w.Header().Set("Allow-Control-Allow-Methods", "POST")
  4. var expense map[string]interface{}
  5. json.NewDecoder(r.Body).Decode(&expense)
  6. var expenseName string
  7. if name, ok := expense["name"]; ok {
  8. expenseName = fmt.Sprintf("%v", name)
  9. } else {
  10. json.NewEncoder(w).Encode("missing required name")
  11. }
  12. var expenseFrequency int
  13. if frequency, ok := expense["frequency"]; ok {
  14. expenseFrequency = int(frequency.(float64))
  15. } else {
  16. expenseFrequency = 1
  17. }
  18. // 处理startDate的规范化
  19. var expenseStartDate *time.Time
  20. if startDate, ok := expense["startDate"]; ok {
  21. startDateString := fmt.Sprintf("%v", startDate)
  22. startDateParsed, err := time.Parse("2006-01-02 15:04:05", startDateString)
  23. if err != nil {
  24. log.Fatal(err)
  25. }
  26. expenseStartDate = &startDateParsed
  27. } else {
  28. json.NewEncoder(w).Encode("missing required startDate")
  29. }
  30. // 处理endDate的规范化
  31. var expenseEndDate *time.Time
  32. if endDate, ok := expense["endDate"]; ok {
  33. endDateString := fmt.Sprintf("%v", endDate)
  34. endDateParsed, err := time.Parse("2006-01-02 15:04:05", endDateString)
  35. if err != nil {
  36. log.Fatal(err)
  37. }
  38. expenseEndDate = &endDateParsed
  39. } else {
  40. expenseEndDate = nil
  41. }
  42. // 处理cost的规范化
  43. var expenseCost primitive.Decimal128
  44. if cost, ok := expense["cost"]; ok {
  45. costString := fmt.Sprintf("%v", cost)
  46. costPrimitive, err := primitive.ParseDecimal128(costString)
  47. if err != nil {
  48. log.Fatal(err)
  49. }
  50. expenseCost = costPrimitive
  51. } else {
  52. json.NewEncoder(w).Encode("missing required cost")
  53. return
  54. }
  55. normalizedExpense := model.Expense{
  56. Name: expenseName,
  57. Frequency: expenseFrequency,
  58. StartDate: expenseStartDate,
  59. EndDate: expenseEndDate,
  60. Cost: expenseCost,
  61. }
  62. // 对结构体变量进行更多操作...
  63. }
英文:

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:

  1. type Expense struct {
  2. Id primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
  3. Name string `json:"name"`
  4. Frequency int `json:"frequency"`
  5. StartDate *time.Time `json:"startDate"`
  6. EndDate *time.Time `json:"endDate,omitempty"`
  7. Cost primitive.Decimal128 `json:"cost"`
  8. Paid []string `json:"paid,omitempty"`
  9. }

Controller in question:

  1. func InsertOneExpense(w http.ResponseWriter, r *http.Request) {
  2. w.Header().Set("Content-Type", "application/json")
  3. w.Header().Set("Allow-Control-Allow-Methods", "POST")
  4. var expense map[string]interface{}
  5. json.NewDecoder(r.Body).Decode(&expense)
  6. var expenseName string
  7. if name, ok := expense["name"]; ok {
  8. expenseName = fmt.Sprintf("%v", name)
  9. } else {
  10. json.NewEncoder(w).Encode("missing required name")
  11. }
  12. var expenseFrequency int
  13. if frequency, ok := expense["frequency"]; ok {
  14. expenseFrequency = int(frequency.(float64))
  15. } else {
  16. expenseFrequency = 1
  17. }
  18. // Handle startDate normalization
  19. var expenseStartDate *time.Time
  20. if startDate, ok := expense["startDate"]; ok {
  21. startDateString := fmt.Sprintf("%v", startDate)
  22. startDateParsed, err := time.Parse("2006-01-02 15:04:05", startDateString)
  23. if err != nil {
  24. log.Fatal(err)
  25. }
  26. expenseStartDate = &startDateParsed
  27. } else {
  28. json.NewEncoder(w).Encode("missing required startDate")
  29. }
  30. // Handle endDate normalization
  31. var expenseEndDate *time.Time
  32. if endDate, ok := expense["endDate"]; ok {
  33. endDateString := fmt.Sprintf("%v", endDate)
  34. endDateParsed, err := time.Parse("2006-01-02 15:04:05", endDateString)
  35. if err != nil {
  36. log.Fatal(err)
  37. }
  38. expenseEndDate = &endDateParsed
  39. } else {
  40. expenseEndDate = nil
  41. }
  42. // Handle cost normaliztion
  43. var expenseCost primitive.Decimal128
  44. if cost, ok := expense["cost"]; ok {
  45. costString := fmt.Sprintf("%v", cost)
  46. costPrimitive, err := primitive.ParseDecimal128(costString)
  47. if err != nil {
  48. log.Fatal(err)
  49. }
  50. expenseCost = costPrimitive
  51. } else {
  52. json.NewEncoder(w).Encode("missing required cost")
  53. return
  54. }
  55. normalizedExpense := model.Expense{
  56. Name: expenseName,
  57. Frequency: expenseFrequency,
  58. StartDate: expenseStartDate,
  59. EndDate: expenseEndDate,
  60. Cost: expenseCost,
  61. }
  62. // Do more things with the struct var...
  63. }

答案1

得分: 1

你可以定义json.UnmarshalJSON接口,然后根据需要手动验证数据。尝试像这样做:

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "strconv"
  6. )
  7. type CoolStruct struct {
  8. MoneyOwed string `json:"money_owed"`
  9. }
  10. // 通过实现json.UnmarshalJSON接口,json包将会将反序列化的工作委托给我们的代码
  11. func (c *CoolStruct) UnmarshalJSON(data []byte) error {
  12. // 将body解析为map[string]*[]byte
  13. raw := map[string]*json.RawMessage{}
  14. if err := json.Unmarshal(data, &raw); err != nil {
  15. return fmt.Errorf("无法解析原始消息映射:%w", err)
  16. }
  17. // 如果我们不知道发送的变量类型,可以将其解析为一个接口
  18. var tempHolder interface{}
  19. err := json.Unmarshal(*raw["money_owed"], &tempHolder)
  20. if err != nil {
  21. return fmt.Errorf("无法从原始消息映射中解析自定义值:%w", err)
  22. }
  23. // 解析后的接口有一个底层类型,使用Go的类型系统来确定所需的类型转换/规范化
  24. switch tempHolder.(type) {
  25. case int64:
  26. // 一旦确定了类型,我们只需将值分配给接收器的字段
  27. c.MoneyOwed = strconv.FormatInt(tempHolder.(int64), 10)
  28. // 可以逐个列出所有类型,也可以作为一个组列出,根据需求而定
  29. case int, int32, float32, float64:
  30. c.MoneyOwed = fmt.Sprint(tempHolder)
  31. case string:
  32. c.MoneyOwed = tempHolder.(string)
  33. default:
  34. fmt.Printf("缺少类型情况:%T\n", tempHolder)
  35. }
  36. // 成功;结构体现在已填充
  37. return nil
  38. }
  39. func main() {
  40. myJson := []byte(`{"money_owed": 123.12}`)
  41. cool := CoolStruct{}
  42. // 在结构体之外,可以像平常一样进行编组/解组
  43. if err := json.Unmarshal(myJson, &cool); err != nil {
  44. panic(err)
  45. }
  46. fmt.Printf("%+v\n", cool)
  47. }

输出:{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:

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "strconv"
  6. )
  7. type CoolStruct struct {
  8. MoneyOwed string `json:"money_owed"`
  9. }
  10. // UnmarshalJSON the json package will delegate deserialization to our code if we implement the json.UnmarshalJSON interface
  11. func (c *CoolStruct) UnmarshalJSON(data []byte) error {
  12. // get the body as a map[string]*[]byte
  13. raw := map[string]*json.RawMessage{}
  14. if err := json.Unmarshal(data, &raw); err != nil {
  15. return fmt.Errorf("unable to unmarshal raw meessage map: %w", err)
  16. }
  17. // if we don't know the variable type sent we can unmarshal to an interface
  18. var tempHolder interface{}
  19. err := json.Unmarshal(*raw["money_owed"], &tempHolder)
  20. if err != nil {
  21. return fmt.Errorf("unable to unmarshal custom value from raw message map: %w", err)
  22. }
  23. // the unmarshalled interface has an underlying type use go's typing
  24. // system to determine type conversions / normalizations required
  25. switch tempHolder.(type) {
  26. case int64:
  27. // once we determine the type of the we just assign the value
  28. // to the receiver's field
  29. c.MoneyOwed = strconv.FormatInt(tempHolder.(int64), 10)
  30. // we could list all individually or as a group; driven by requirements
  31. case int, int32, float32, float64:
  32. c.MoneyOwed = fmt.Sprint(tempHolder)
  33. case string:
  34. c.MoneyOwed = tempHolder.(string)
  35. default:
  36. fmt.Printf("missing type case: %T\n", tempHolder)
  37. }
  38. // success; struct is now populated
  39. return nil
  40. }
  41. func main() {
  42. myJson := []byte(`{"money_owed": 123.12}`)
  43. cool := CoolStruct{}
  44. // outside of your struct you marshal/unmarshal as normal
  45. if err := json.Unmarshal(myJson, &cool); err != nil {
  46. panic(err)
  47. }
  48. fmt.Printf("%+v\n", cool)
  49. }

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:

确定