Home >Backend Development >Golang >How to best normalize JSON data into API structs in Go

How to best normalize JSON data into API structs in Go

王林
王林forward
2024-02-13 18:06:21972browse

如何最好地将 JSON 数据规范化为 Go 中的 API 结构体

php editor Xigua here brings a streamlined guide on how to normalize JSON data into API structures in Go. In modern web applications, working with JSON data is a common task. As a powerful back-end language, Go language provides a concise and flexible way to process JSON data. This article will introduce how to use structures in the Go language to normalize JSON data so that they can be better processed and manipulated. Whether you are a beginner or an experienced developer, this article will provide you with useful tips and practical examples. let's start!

Question content

I am fairly new to go and am trying to determine if there is a cleaner way to accomplish the normalization of json data from the front end (js) to my api. To ensure that the correct types are used when creating variables from the struct (model.expense), I dump the payload into a map, then normalize and save back to the struct. If anyone could teach me a better way to handle this I would be very grateful! Thanks in advance!

Model. Fee structure:

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"`
}

Problematic controller:

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...
}

Workaround

You can define the json.unmarshaljson interface and then manually validate the data if needed. 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://www.php.cn/link/87ca4eb840b6f78e3b6d6b418c0fef40

The above is the detailed content of How to best normalize JSON data into API structs in Go. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete