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)
}
回到顶部