Golang中JSON标签无效的问题如何解决
Golang中JSON标签无效的问题如何解决 假设我们有一个结构体:
type DailyExchangeDetails struct {
Date string `json:"date"`
Currency map[string]float64 `json:"btc"`
}
但是,JSON属性(字段名)可能不同——它可能是“btc”,但也可能是“eur”或“pln”。如何创建一个通用的结构体,其JSON标签能够覆盖这种情况?问题在于,如果没有正确的标签(例如:`json:"pln"`),JSON编码就无法正常工作。此外,如果我省略JSON标签,它仍然无法正常工作。
这是用于编码的代码:
func fromJSON[T any](res *http.Response, target *T) error {
//
buf := new(bytes.Buffer)
buf.ReadFrom(res.Body)
err := json.Unmarshal(buf.Bytes(), target)
if err != nil {
log.Print(err)
log.Fatalf("Error unmarshal")
}
return err
}
这是JSON文件的一个片段:
{
"date": "2024-05-15",
"btc": {
"$myro": 388814.57395719,
"$wen": 399391074.4242139,
"00": 855345.08008341,
"0x0": 205498.96296352,
"1000sats": 237075658.30367133,
"10set": 62400.24253448,
"1inch": 173185.53022438,
"aave": 765.26611448,
"abt": 15467.94403195
}
}
我注意到问题出在map上。这是解组后返回的结构体:
{2024-05-16 map[]}。如你所见,map不知为何是空的。
更多关于Golang中JSON标签无效的问题如何解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html
是的,我现在明白了。在这种情况下,你可以寻找支持多个标签定义的第三方 JSON 库。使用标准库的解决方案将是编写你自己的反序列化方法,或者解码到 map[string]any 而不是结构体。
func main() {
fmt.Println("hello world")
}
如果不为结构体字段使用显式标签,那么 json 会默认字段名等于结构体字段名。在你的示例中,如果从结构体中移除标签,它会期望 json 文件中的字段是 date 和 currency。
如果你添加JSON标签,它就能正常工作。如果不添加,它就无法工作。正如我所说,JSON字段名称可以不同,但结构是相同的。我们有一个字符串和一个映射。
{
"date": "2024-05-15",
"pln": {
"$myro": 388814.57395719,
"$wen": 399391074.4242139,
"00": 855345.08008341,
"0x0": 205498.96296352,
"1000sats": 237075658.30367133,
"10set": 62400.24253448,
"1inch": 173185.53022438,
"aave": 765.26611448,
"abt": 15467.94403195
}
}
如果你把货币改为PLN,它就无法工作。
假设我们有一个结构体:
type DailyExchangeDetails struct {
Date string `json:"date"`
Currency map[string]float64 `json:"btc"`
}
但是 JSON 属性、字段名称可能不同——可能是 “btc”,但也可能是 “eur” 或 “pln”。如何创建一个通用的结构体,其 json 标签能够覆盖这种情况?问题在于,如果没有正确的标签,例如 json:"pln",JSON 的编码就无法正常工作。此外,如果我省略 JSON 标签,它仍然无法正常工作。
这是用于编码的代码:
func fromJSON[T any](res *http.Response, target *T) error {
//
buf := new(bytes.Buffer)
buf.ReadFrom(res.Body)
err := json.Unmarshal(buf.Bytes(), target)
if err != nil {
log.Print(err)
log.Fatalf("Error unmarshal")
}
return err
}
这是一个 JSON 文件的片段:
{
"date": "2024-05-15",
"btc": {
"$myro": 388814.57395719,
"$wen": 399391074.4242139,
"00": 855345.08008341,
"0x0": 205498.96296352,
"1000sats": 237075658.30367133,
"10set": 62400.24253448,
"1inch": 173185.53022438,
"aave": 765.26611448,
"abt": 15467.94403195
}
}
我注意到问题出在 map 上。这是从 unmarshal 返回的结构体:
{2024-05-16 map[]}。如你所见,map 不知为何是空的。
感谢你帮助我,我之前很困惑。你让我很开心!
问题出在结构体定义与JSON数据结构不匹配。JSON中的btc字段是一个对象(键值对),但你的结构体将其定义为map[string]float64,这本身是正确的。然而,实际问题是JSON中的btc对象包含嵌套的键值对,而你的结构体期望的是直接映射。
根据你提供的JSON片段,btc字段的值是一个对象,其键是货币代码(如$myro、$wen等),值是浮点数。因此,结构体定义是正确的。问题可能在于JSON数据的实际结构与示例不符,或者存在其他解析问题。
以下是一个修正后的示例,确保结构体与JSON匹配,并添加了错误处理:
package main
import (
"encoding/json"
"fmt"
"log"
)
type DailyExchangeDetails struct {
Date string `json:"date"`
Currency map[string]float64 `json:"btc"` // JSON字段名固定为"btc"
}
func main() {
// 示例JSON数据(与你的片段一致)
jsonData := `{
"date": "2024-05-15",
"btc": {
"$myro": 388814.57395719,
"$wen": 399391074.4242139,
"00": 855345.08008341
}
}`
var result DailyExchangeDetails
err := json.Unmarshal([]byte(jsonData), &result)
if err != nil {
log.Fatalf("Error unmarshaling JSON: %v", err)
}
fmt.Printf("Date: %s\n", result.Date)
fmt.Printf("Currency map: %v\n", result.Currency)
// 输出示例:Currency map: map[$myro:388814.57395719 $wen:3.993910744242139e+08 00:855345.08008341]
}
如果你的JSON中btc字段名是动态的(例如可能是eur或pln),则不能使用固定标签。此时,你需要使用map[string]interface{}进行通用解析,或者使用自定义解组逻辑。以下是使用map[string]interface{}的解决方案:
package main
import (
"encoding/json"
"fmt"
"log"
)
func main() {
// 示例JSON,其中货币字段名可能是btc、eur或pln
jsonData := `{
"date": "2024-05-15",
"pln": {
"$myro": 388814.57395719,
"$wen": 399391074.4242139
}
}`
var rawData map[string]interface{}
err := json.Unmarshal([]byte(jsonData), &rawData)
if err != nil {
log.Fatalf("Error unmarshaling JSON: %v", err)
}
date := rawData["date"].(string)
fmt.Printf("Date: %s\n", date)
// 遍历所有键,找到货币字段(假设除"date"外都是货币字段)
for key, value := range rawData {
if key != "date" {
currencyMap := value.(map[string]interface{})
fmt.Printf("Currency field '%s': %v\n", key, currencyMap)
}
}
}
如果必须使用结构体,且货币字段名动态变化,可以结合使用json.RawMessage和自定义解组:
package main
import (
"encoding/json"
"fmt"
"log"
)
type DailyExchangeDetails struct {
Date string `json:"date"`
Currency json.RawMessage // 原始JSON数据,用于动态字段
}
func main() {
jsonData := `{
"date": "2024-05-15",
"eur": {
"$myro": 388814.57395719,
"$wen": 399391074.4242139
}
}`
var result DailyExchangeDetails
err := json.Unmarshal([]byte(jsonData), &result)
if err != nil {
log.Fatalf("Error unmarshaling JSON: %v", err)
}
fmt.Printf("Date: %s\n", result.Date)
// 动态解析Currency字段
var currencyMap map[string]float64
err = json.Unmarshal(result.Currency, ¤cyMap)
if err != nil {
log.Fatalf("Error unmarshaling currency: %v", err)
}
fmt.Printf("Currency map: %v\n", currencyMap)
}
对于你的fromJSON函数,确保传入的target参数是正确类型的指针。例如:
func fromJSON[T any](res *http.Response, target *T) error {
defer res.Body.Close()
decoder := json.NewDecoder(res.Body)
err := decoder.Decode(target)
if err != nil {
return fmt.Errorf("error unmarshaling JSON: %w", err)
}
return nil
}
// 使用示例
var details DailyExchangeDetails
err := fromJSON(response, &details)
检查你的实际JSON数据是否与结构体匹配。如果问题仍然存在,请验证HTTP响应体中的JSON是否与示例一致,或者是否有额外的嵌套层级。


