Golang中如何声明并解析包含不同结构体值的map

Golang中如何声明并解析包含不同结构体值的map

package main

import (
	"fmt"
	"encoding/json"
	"log"
)

type  X struct {
    A int  `json:"a,omitempty"`
    C int `json:"c,omitempty"`
} 

type  Y struct {
    B int
} 

func main() {
	
	type M interface {
	}
	m := map[string]M{ "k1": X{A:33}, "k2": Y{B:2}   }
	
	data := `{"c": 22}`
	
 	cc := m["k1"]
	fmt.Printf("%#v", cc)
	fmt.Println("--------------   ")
	err := json.Unmarshal([]byte(data), &cc)
	fmt.Printf("%#v", cc)
	log.Println(m)
	fmt.Println(err)
	//fmt.Println("Hello, playground")
}

为什么我无法解组它? 我的输出

main.X{A:33, C:0}--------------   
map[string]interface {}{"c":22}2009/11/10 23:00:00 map[k1:{33 0} k2:{2}]
<nil>

更多关于Golang中如何声明并解析包含不同结构体值的map的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

是的,这就是唯一的问题……cc 怎么会变成 map[string]interface {}{"c":22}

更多关于Golang中如何声明并解析包含不同结构体值的map的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好,@Mukul_Dilwaria,你的代码确实在解组 cc;你的 Printf 显示 cc 中的新值从解组后的 JSON {"c": 22} 变成了 map[string]interface{}。你能说明一下你原本期望的结果吗?

Mukul_Dilwaria:

type M interface { } m := map[string]M{ "k1": X{A:33}, "k2": Y{B:2} }

m的类型是map[string]M,我将其称为与map[string]interface{}类型"等效"。

Mukul_Dilwaria:

cc := m["k1"]

这里cc的类型是M,相当于interface{},这是一个接口类型。Unmarshal函数的文档说明:

要将JSON解组到接口值中,Unmarshal会在接口值中存储以下类型之一:

bool,对应JSON布尔值
float64,对应JSON数字
string,对应JSON字符串
[]interface{},对应JSON数组
map[string]interface{},对应JSON对象
nil,对应JSON null

由于您正在将JSON解组到接口类型中,并且要解组的JSON值({"c": 22})是一个JSON对象,因此结果类型如文档所述是map[string]interface{}

问题在于您使用了空接口interface{}(在代码中定义为M)来存储不同类型的结构体值。当JSON解组时,Go无法知道原始类型信息,因此会将数据解析为map[string]interface{}而不是原始结构体类型。

以下是修正后的代码:

package main

import (
	"encoding/json"
	"fmt"
	"log"
)

type X struct {
	A int `json:"a,omitempty"`
	C int `json:"c,omitempty"`
}

type Y struct {
	B int
}

func main() {
	// 方法1:使用类型断言
	m := map[string]interface{}{"k1": X{A: 33}, "k2": Y{B: 2}}
	
	data := `{"c": 22}`
	
	// 获取k1的值并进行类型断言
	if x, ok := m["k1"].(X); ok {
		err := json.Unmarshal([]byte(data), &x)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("方法1结果: %#v\n", x)
		m["k1"] = x
	}
	
	log.Println("方法1 map:", m)
	
	// 方法2:重新创建结构体实例
	m2 := map[string]interface{}{"k1": X{A: 33}, "k2": Y{B: 2}}
	
	var x2 X
	x2 = m2["k1"].(X) // 类型断言
	err := json.Unmarshal([]byte(data), &x2)
	if err != nil {
		log.Fatal(err)
	}
	m2["k1"] = x2
	fmt.Printf("方法2结果: %#v\n", x2)
	log.Println("方法2 map:", m2)
	
	// 方法3:使用自定义解组逻辑
	m3 := map[string]interface{}{"k1": X{A: 33}, "k2": Y{B: 2}}
	
	switch v := m3["k1"].(type) {
	case X:
		err := json.Unmarshal([]byte(data), &v)
		if err != nil {
			log.Fatal(err)
		}
		m3["k1"] = v
		fmt.Printf("方法3结果: %#v\n", v)
	default:
		fmt.Printf("未知类型: %T\n", v)
	}
	
	log.Println("方法3 map:", m3)
}

输出结果:

方法1结果: main.X{A:33, C:22}
2009/11/10 23:00:00 方法1 map: map[k1:{33 22} k2:{2}]
方法2结果: main.X{A:33, C:22}
2009/11/10 23:00:00 方法2 map: map[k1:{33 22} k2:{2}]
方法3结果: main.X{A:33, C:22}
2009/11/10 23:00:00 方法3 map: map[k1:{33 22} k2:{2}]

关键点:

  1. 当使用空接口存储值时,JSON解组会丢失原始类型信息
  2. 需要使用类型断言来获取具体的结构体类型
  3. 解组操作必须在具体的类型实例上进行,而不是接口类型

在您的原始代码中,ccinterface{}类型,JSON解组时Go会创建一个新的map[string]interface{}来存储数据,而不是更新原始的X结构体。

回到顶部