Golang中JSON反序列化遇到问题求助

Golang中JSON反序列化遇到问题求助 我想反序列化这个JSON:

{
    "BSR Address": "10.0.3.5",
    "228.0.0.0/8": {
        "10.0.3.5": {
            "Rp Address": "10.0.3.5",
            "Rp HoldTime": 65535,
            "Rp Priority": 1,
            "Hash Val": 840583365
        },
        "Pending RP count": 100
    }
}

但是如果没有JSON标签,它就无法工作,而这些标签是在运行时生成的。


更多关于Golang中JSON反序列化遇到问题求助的实战教程也可以访问 https://www.itying.com/category-94-b0.html

8 回复

到目前为止你尝试了什么?

通常,对于具有动态键的对象,我会使用 map[string]T;如果值的类型是异构的,我会使用 map[string]interface{}

func main() {
    fmt.Println("hello world")
}

更多关于Golang中JSON反序列化遇到问题求助的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我需要获取键值以及结构体值,但内部的 map[string]struct 无法正确反序列化。

我也尝试使用了在线 JSON 转 Go 转换器自动生成的结构体,但如果我移除其中的 JSON 标签,它就会失败。

我没有看到任何结构体。我只看到一个 JSON 对象。

如果你确实有一个想要解组到的结构体,你需要给它打上标签。如果你不能打标签,就必须像上面提到的那样,解组到一个更动态的类型。

我目前正在使用移动设备,因此无法轻易给你一个示例。

// 代码示例应放在这里

再次强调,如果你想反序列化到一个结构体中,必须指定所有字段。由于你似乎无法做到这一点,就必须使用一个更动态的根类型。


或者,有方法可以编写比“默认”实现更具体的反序列化器。

这样的实现可以将已知字段存储在结构体的字段中,并将动态字段存储在结构体另一个字段的映射中。

我还没有使用过这种专门的反序列化器,但我见过它们在实际中的应用。

哦,还有另一个非常有用的资源我忘了提,它可以帮你节省时间:尝试将你的 JSON 粘贴到 JSON-to-Go。在处理来自互联网的随机 JSON 时,这总是一个很好的初步排查步骤。 表情

我尝试了这些结构体:

type RP struct {
	RpAddress  string `json:"Rp Address"`
	RpHoldTime int    `json:"Rp HoldTime"`
	RpPriority int    `json:"Rp Priority"`
	HashVal    int    `json:"Hash Val"`
}
type A struct {
	Rpinfo         map[string]RP
	PendingRPCount int `json:"Pending RP count"`
}
type final struct {
	Bsrinfo    map[string]A
	BSRAddress string `json:"BSR Address"`
}

假设你的输入数据中JSON是一个变量:

// 从你的示例中清理后的JSON
exampleJSON := []byte(`{
    "BSR Address": "10.0.3.5",
    "228.0.0.0/8": {
        "10.0.3.5": {
            "Rp Address": "10.0.3.5",
            "Rp HoldTime": 65535,
            "Rp Priority": 1,
            "Hash Val": 840583365
        },
        "Pending RP count": 100
    }
}`)

你可以将其解组到 map[string]interface{} 中,这没有问题:

func main() {
	// 解组到通用类型
	var dat map[string]interface{}
	if err := json.Unmarshal(exampleJSON, &dat); err != nil {
		panic(err)
	}

	fmt.Println("打印键值对")
	for k, v := range dat {
		fmt.Println(k, ":", v)
	}
}

输出:

打印键值对
BSR Address : 10.0.3.5
228.0.0.0/8 : map[10.0.3.5:map[Hash Val:8.40583365e+08 Rp Address:10.0.3.5 Rp HoldTime:65535 Rp Priority:1] Pending RP count:100]

当你说“我需要键值和结构体值,但内部的 map[string]struct 无法正确解组”时,我相信你的意思是“我希望能够从我的代码中访问子映射和值”,这只是一个简单的类型断言问题。让我们更新上面的示例,调用一个使用反射来断言子映射类型并打印它们的函数:

func main() {
	// 解组到通用类型
	var dat map[string]interface{}
	if err := json.Unmarshal(exampleJSON, &dat); err != nil {
		panic(err)
	}
	fmt.Println("更聪明地使用反射来打印值")
	printValues("", dat)
}

func printValues(prefix string, values map[string]interface{}) {
	for k, v := range values {
		// 如果我们的值本身是一个映射,则递归打印其值
		if reflect.ValueOf(v).Kind() == reflect.Map {
			fmt.Println(prefix, k, ":")
			printValues(prefix+"\t", v.(map[string]interface{}))
		} else {
			// 否则,只需打印键/值对以及前缀
			fmt.Println(prefix, k, ":", v)
		}
	}
}

这将得到以下结果:

 更聪明地使用反射来打印值
 BSR Address : 10.0.3.5
 228.0.0.0/8 :
	 10.0.3.5 :
		 Rp Address : 10.0.3.5
		 Rp HoldTime : 65535
		 Rp Priority : 1
		 Hash Val : 8.40583365e+08
	 Pending RP count : 100

这是我为你创建的Go Playground链接,你可以自己运行它:

https://go.dev/play/p/1wqxTok7TIP

尝试更改硬编码的JSON,看看会发生什么。同时,请务必查看这个优秀的StackOverflow问题及其相关答案,以了解其他方法。如果这还不能满足你的需求,我认为你需要提供更多示例代码以及你试图实现的目标。

在Go中处理动态键名的JSON时,可以使用map[string]interface{}或自定义json.RawMessage来解析。以下是两种解决方案:

方案1:使用map[string]interface{}进行通用解析

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    jsonStr := `{
        "BSR Address": "10.0.3.5",
        "228.0.0.0/8": {
            "10.0.3.5": {
                "Rp Address": "10.0.3.5",
                "Rp HoldTime": 65535,
                "Rp Priority": 1,
                "Hash Val": 840583365
            },
            "Pending RP count": 100
        }
    }`

    var data map[string]interface{}
    if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
        panic(err)
    }

    // 访问BSR Address
    bsrAddr := data["BSR Address"].(string)
    fmt.Printf("BSR Address: %s\n", bsrAddr)

    // 动态访问子网信息
    subnetData := data["228.0.0.0/8"].(map[string]interface{})
    for key, value := range subnetData {
        if key == "Pending RP count" {
            count := int(value.(float64))
            fmt.Printf("Pending RP count: %d\n", count)
        } else {
            rpInfo := value.(map[string]interface{})
            fmt.Printf("RP %s: %+v\n", key, rpInfo)
        }
    }
}

方案2:使用json.RawMessage延迟解析

package main

import (
    "encoding/json"
    "fmt"
)

type RPInfo struct {
    RpAddress  string `json:"Rp Address"`
    RpHoldTime int    `json:"Rp HoldTime"`
    RpPriority int    `json:"Rp Priority"`
    HashVal    int    `json:"Hash Val"`
}

type SubnetData struct {
    PendingRPCount int               `json:"Pending RP count"`
    RPDetails      map[string]RPInfo `json:"-"`
}

func main() {
    jsonStr := `{
        "BSR Address": "10.0.3.5",
        "228.0.0.0/8": {
            "10.0.3.5": {
                "Rp Address": "10.0.3.5",
                "Rp HoldTime": 65535,
                "Rp Priority": 1,
                "Hash Val": 840583365
            },
            "Pending RP count": 100
        }
    }`

    var raw map[string]json.RawMessage
    if err := json.Unmarshal([]byte(jsonStr), &raw); err != nil {
        panic(err)
    }

    // 解析BSR Address
    var bsrAddr string
    json.Unmarshal(raw["BSR Address"], &bsrAddr)
    fmt.Printf("BSR Address: %s\n", bsrAddr)

    // 动态解析子网数据
    var subnetRaw map[string]json.RawMessage
    json.Unmarshal(raw["228.0.0.0/8"], &subnetRaw)

    subnetData := SubnetData{}
    subnetData.RPDetails = make(map[string]RPInfo)

    for key, value := range subnetRaw {
        if key == "Pending RP count" {
            json.Unmarshal(value, &subnetData.PendingRPCount)
        } else {
            var rpInfo RPInfo
            json.Unmarshal(value, &rpInfo)
            subnetData.RPDetails[key] = rpInfo
        }
    }

    fmt.Printf("Pending RP count: %d\n", subnetData.PendingRPCount)
    for rpAddr, info := range subnetData.RPDetails {
        fmt.Printf("RP %s: %+v\n", rpAddr, info)
    }
}

方案3:自定义UnmarshalJSON方法

package main

import (
    "encoding/json"
    "fmt"
)

type NetworkData struct {
    BSRAddress string                     `json:"BSR Address"`
    Subnets    map[string]json.RawMessage `json:"-"`
}

func (n *NetworkData) UnmarshalJSON(data []byte) error {
    var raw map[string]json.RawMessage
    if err := json.Unmarshal(data, &raw); err != nil {
        return err
    }

    // 处理固定字段
    if v, ok := raw["BSR Address"]; ok {
        json.Unmarshal(v, &n.BSRAddress)
        delete(raw, "BSR Address")
    }

    // 剩余的都是动态子网
    n.Subnets = raw
    return nil
}

func main() {
    jsonStr := `{
        "BSR Address": "10.0.3.5",
        "228.0.0.0/8": {
            "10.0.3.5": {
                "Rp Address": "10.0.3.5",
                "Rp HoldTime": 65535,
                "Rp Priority": 1,
                "Hash Val": 840583365
            },
            "Pending RP count": 100
        }
    }`

    var data NetworkData
    if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
        panic(err)
    }

    fmt.Printf("BSR Address: %s\n", data.BSRAddress)
    
    for subnet, rawSubnet := range data.Subnets {
        fmt.Printf("Subnet %s: %s\n", subnet, string(rawSubnet))
    }
}

这些方案都能处理运行时生成的动态键名。第一种方案最简单直接,第二种提供了更好的类型安全,第三种最灵活。根据你的具体需求选择合适的方法。

回到顶部