English
Solving the binding problem from an array of numeric strings to an int64 slice in the Gin framework
In actual development, we often encounter the situation where the client passes an array of numbers in string format, while the server needs to bind it to an int64 slice. This article will explain in detail how to elegantly solve this problem using the Gin framework.
Problem Background
When using the Gin framework for JSON binding, if the client passes numbers in string array format (e.g.{"ids": ["1641850092312858666", "1641850092312858667"]}), while the server-side struct defines []int64 type, you will encounter the following error:
txt
Error #01: json: cannot unmarshal string into Go struct field RequiredIdsInt64Params.ids of type int64Solution
Custom type Int64Slice
We can do this by defining a custom type.Int64Slice and implement json.Unmarshaler Interface to solve this problem:
go
// Int64Slice is a custom type used to handle the conversion from a string array to an int64 array
// swagger:type []int64 [28994660812304,28994660812305]
type Int64Slice []int64
// UnmarshalJSON implements custom JSON deserialization logic
func (i *Int64Slice) UnmarshalJSON(data []byte) error {
// First, try to parse the data as a string array
var strSlice []string
if err := json.Unmarshal(data, &strSlice); err != nil {
return err
}
// Convert a string array to an int64 array
var intSlice Int64Slice
for _, str := range strSlice {
intVal, err := strconv.ParseInt(str, 10, 64)
if err != nil {
return err
}
intSlice = append(intSlice, intVal)
}
// Assign the converted int64 array to Int64Slice
*i = intSlice
return nil
}
// MarshalJSON implements custom JSON serialization logic.
func (i Int64Slice) MarshalJSON() ([]byte, error) {
// Convert an int64 array to a string array
var strSlice []string
for _, val := range i {
strSlice = append(strSlice, strconv.FormatInt(val, 10))
}
// Serialize a string array into JSON
return json.Marshal(strSlice)
}Use custom types
When defining a struct, use our custom type:
go
type RequiredIdsInt64Params struct {
// Ids.
//
// required: true
// example: ["1641850092312858666", "1641850092312858667"]
Ids Int64Slice `json:"ids" binding:"required" form:"ids" uri:"ids"`
}Binding example
go
func HandleRequest(ctx *gin.Context) {
var params RequiredIdsInt64Params
if err := ctx.ShouldBindJSON(¶ms); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Now params.Ids is already the correct []int64 type.
fmt.Printf("Received IDs: %v\n", params.Ids)
}Solution advantages
Type safety: Ensure that the received data is a valid int64 type
Error handling: Automatically handles conversion from string to int64 and provides error checking
Bidirectional support: Also implements the MarshalJSON method, supporting serialization back to string format
Swagger compatible Ensure API documentation displays correctly through comments.
Reusability This custom type can be reused throughout the project.
Other precautions
Performance considerations: For very large arrays, consider preallocating slice capacity.
error message: You can customize more user-friendly error messages
Boundary check: Additional validation of the numeric range can be added
Summary
By implementing custom types and JSON serialization/deserialization interfaces, we can elegantly solve the binding problem from string arrays to int64 slices in the Gin framework. This approach both maintains code cleanliness and provides good type safety and error handling mechanisms.
This pattern is not only applicable to the int64 type but can also be generalized to other type conversion scenarios that require special handling.