Golang结构体中any和泛型的使用指南
Golang结构体中any和泛型的使用指南 我有以下代码:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
type Number interface {
int64 | float64
}
type SKUcard[number Number] struct {
BarCode, SKUCode any
VendorCode, RegistrationDate, VendorName, BrandName, ContactPerson string
ContactNumber any
ItemName, ItemImage string
NetWeight, CartoonPack, StorageTemperature, ShelfLife, ShelfPrice, KottofCost number
SupplyType, CoveredAreas, MinimumOrderQty, ContractDate, ReturnPolicy, Notes string
InActive string
}
func main() {
req, _ := http.NewRequest("GET", "https://script.google.com/macros/s/AKfycbzw0TKWycxeB5sx1wIefAiEHeYQt2mVuM-NAZTccxedhyntdv8FvcUteOZ2k03wRHGE/exec?", nil)
q := req.URL.Query()
q.Add("barcode", "6287029390129")
//q.Add("another_thing", "foo & bar")
req.URL.RawQuery = q.Encode()
fmt.Println(req.URL.String())
// url := "https://script.google.com/macros/s/AKfycbzw0TKWycxeB5sx1wIefAiEHeYQt2mVuM-NAZTccxedhyntdv8FvcUteOZ2k03wRHGE/exec?barcode=6287029390129"
// resp, err := http.Get(req.URL.String())
resp, err := http.DefaultClient.Do(req)
_ = req.URL.RawQuery
if err != nil {
log.Fatalln(err)
}
//We Read the response body on the line below.
/* body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalln(err)
}
//Convert the body to type string
sb := string(body)
log.Printf(sb) */
// var target interface{}
var target = new(SKUcard[float64])
err = json.NewDecoder(resp.Body).Decode(target)
if err != nil {
fmt.Println("err:", err)
}
str := fmt.Sprintf("%v", target.BarCode)
fmt.Println(str)
fmt.Println(reflect.TypeOf(target.BarCode))
}
输出如下:
6.287029390129e+12
float64
我的问题是,我从服务器获取的barcode应该是一个string,但在某些情况下它是一个number。
在上面我的结构体中,如果我将barcode定义为字符串,那么我会遇到以下运行时错误:
type SKUcard[number Number] struct {
BarCode, SKUCode string
...
}
// 错误:
err: json: cannot unmarshal number into Go struct field SKUcard[float64].BarCode of type string
如果我像上面完整代码中那样将barcode定义为any,并且服务器代码在该字段中输入的是数字,那么我会得到科学计数法格式,即:
6.287029390129e+12
在上面的代码中,输出应该是6287029390129,类型为字符串。
更多关于Golang结构体中any和泛型的使用指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html
3 回复
你是否考虑过使用 json.Number?
使用起来有点笨拙,但相对稳定。
当然,如果你指定 float,你得到的就是一个浮点数。众所周知,浮点数是不精确的,在任何要求精确性的地方都不应该使用。
更多关于Golang结构体中any和泛型的使用指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
NobbZ:
虽然用起来有点笨拙,但相对稳定。
谢谢,我尝试了下面的接收器,它对我有效:
type AnyString string
func (s *AnyString) UnmarshalJSON(data []byte) error {
fmt.Println(fmt.Sprintf("%s", data))
const layout = "2006-01-02T03:04:05.999Z"
if data[0] != '"' { // not string, so probably int, make it a string by wrapping it in double quotes
data = []byte(`"` + string(data) + `"`)
} else if len(data) > 4 { // clean date and return date part only without time
timestamp := fmt.Sprintf("%s", data)
stamp, err := strconv.Unquote(timestamp) // my date appeared as: "\"2022-04-28T12:32:27.242Z\""
if err != nil {
fmt.Println(err)
}
t, error := time.Parse(layout, stamp)
if error != nil {
fmt.Println(error)
} else {
fmt.Println("valid!:", t)
y, m, d := t.Date()
fmt.Printf("%v/%v/%v", d, m, y)
data = []byte(`"` + fmt.Sprintf("%v/%v/%v", d, m, y) + `"`)
}
}
// unmarshal as plain string
return json.Unmarshal(data, (*string)(s))
}
在我的结构体中,我使用了:
type SKUcard[number Number] struct {
BarCode, SKUCode, VendorCode, RegistrationDate AnyString
...
}
在Golang中处理JSON数字和字符串混合类型时,可以使用json.Number类型。以下是修改后的代码:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
)
type Number interface {
int64 | float64
}
type SKUcard[number Number] struct {
BarCode, SKUCode json.Number
VendorCode, RegistrationDate, VendorName, BrandName, ContactPerson string
ContactNumber any
ItemName, ItemImage string
NetWeight, CartoonPack, StorageTemperature, ShelfLife, ShelfPrice, KottofCost number
SupplyType, CoveredAreas, MinimumOrderQty, ContractDate, ReturnPolicy, Notes string
InActive string
}
func main() {
req, _ := http.NewRequest("GET", "https://script.google.com/macros/s/AKfycbzw0TKWycxeB5sx1wIefAiEHeYQt2mVuM-NAZTccxedhyntdv8FvcUteOZ2k03wRHGE/exec?", nil)
q := req.URL.Query()
q.Add("barcode", "6287029390129")
req.URL.RawQuery = q.Encode()
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatalln(err)
}
var target = new(SKUcard[float64])
err = json.NewDecoder(resp.Body).Decode(target)
if err != nil {
fmt.Println("err:", err)
}
// 获取字符串形式的条码
barcodeStr := target.BarCode.String()
fmt.Println("Barcode as string:", barcodeStr)
// 如果需要去除科学计数法格式
if strings.Contains(barcodeStr, "e+") {
// 转换为float64再格式化为字符串
if f, err := target.BarCode.Float64(); err == nil {
fmt.Printf("Barcode formatted: %.0f\n", f)
}
}
// 也可以直接使用Int64
if intVal, err := target.BarCode.Int64(); err == nil {
fmt.Printf("Barcode as int64: %d\n", intVal)
}
}
或者,如果需要更灵活的处理,可以自定义JSON unmarshal:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
)
type Number interface {
int64 | float64
}
type FlexibleString string
func (fs *FlexibleString) UnmarshalJSON(data []byte) error {
var v interface{}
if err := json.Unmarshal(data, &v); err != nil {
return err
}
switch val := v.(type) {
case string:
*fs = FlexibleString(val)
case float64:
*fs = FlexibleString(strconv.FormatFloat(val, 'f', -1, 64))
case int64:
*fs = FlexibleString(strconv.FormatInt(val, 10))
case int:
*fs = FlexibleString(strconv.Itoa(val))
default:
*fs = FlexibleString(fmt.Sprintf("%v", val))
}
return nil
}
type SKUcard[number Number] struct {
BarCode, SKUCode FlexibleString
VendorCode, RegistrationDate, VendorName, BrandName, ContactPerson string
ContactNumber any
ItemName, ItemImage string
NetWeight, CartoonPack, StorageTemperature, ShelfLife, ShelfPrice, KottofCost number
SupplyType, CoveredAreas, MinimumOrderQty, ContractDate, ReturnPolicy, Notes string
InActive string
}
func main() {
req, _ := http.NewRequest("GET", "https://script.google.com/macros/s/AKfycbzw0TKWycxeB5sx1wIefAiEHeYQt2mVuM-NAZTccxedhyntdv8FvcUteOZ2k03wRHGE/exec?", nil)
q := req.URL.Query()
q.Add("barcode", "6287029390129")
req.URL.RawQuery = q.Encode()
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatalln(err)
}
var target = new(SKUcard[float64])
err = json.NewDecoder(resp.Body).Decode(target)
if err != nil {
fmt.Println("err:", err)
}
fmt.Println("Barcode:", string(target.BarCode))
fmt.Println("Type: string")
}
对于泛型结构体中的any字段,如果需要类型安全,可以定义具体的类型约束:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
type Number interface {
int64 | float64
}
type ContactType interface {
string | int64 | float64
}
type SKUcard[number Number, contact ContactType] struct {
BarCode, SKUCode json.Number
VendorCode, RegistrationDate, VendorName, BrandName, ContactPerson string
ContactNumber contact
ItemName, ItemImage string
NetWeight, CartoonPack, StorageTemperature, ShelfLife, ShelfPrice, KottofCost number
SupplyType, CoveredAreas, MinimumOrderQty, ContractDate, ReturnPolicy, Notes string
InActive string
}
func main() {
req, _ := http.NewRequest("GET", "https://script.google.com/macros/s/AKfycbzw0TKWycxeB5sx1wIefAiEHeYQt2mVuM-NAZTccxedhyntdv8FvcUteOZ2k03wRHGE/exec?", nil)
q := req.URL.Query()
q.Add("barcode", "6287029390129")
req.URL.RawQuery = q.Encode()
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatalln(err)
}
// 根据实际情况选择类型
var target = new(SKUcard[float64, string])
err = json.NewDecoder(resp.Body).Decode(target)
if err != nil {
fmt.Println("err:", err)
}
fmt.Println("Barcode:", target.BarCode.String())
fmt.Println("ContactNumber:", target.ContactNumber)
}

