如何使json.Unmarshal正确处理包含结构体的interface{}?

huangapple go评论85阅读模式
英文:

How to make json.Unmarshal deal with interface{} correctly which contains an struct?

问题

你可以尝试将Data()函数返回一个指向data字段的指针,然后在SetData()函数中将新数据解析为相应的类型,并将其赋值给data字段。这样,你就可以在Data()函数中返回data字段的指针,而不必修改其他代码。

以下是修改后的代码示例:

type IDataContainer interface {
    Data() interface{}
    SetData(newData interface{})
}

type DataA struct {
    Name string `json:"name"`
}

type StructA struct {
    data *DataA
}

func (a *StructA) Data() interface{} {
    return a.data
}

func (a *StructA) SetData(newData interface{}) {
    a.data = newData.(*DataA)
}

type DataB struct {
    Age int `json:"age"`
}

type StructB struct {
    data *DataB
}

func (b *StructB) Data() interface{} {
    return b.data
}

func (b *StructB) SetData(newData interface{}) {
    b.data = newData.(*DataB)
}

func main() {
    jsonDatas := [][]byte{[]byte(`{"name":"NEW"}`), []byte(`{"age":30}`)}
    dataContainers := []IDataContainer{&StructA{}, &StructB{}}
    for i, jsonData := range jsonDatas {
        dataContainer := dataContainers[i]
        data := dataContainer.Data()
        json.Unmarshal(jsonData, data)
    }
    fmt.Println(dataContainers[0], dataContainers[1])
}

在修改后的代码中,data字段被定义为指针类型,并且Data()函数返回data字段的指针。在SetData()函数中,我们将新数据解析为相应的类型,并将其赋值给data字段的指针。

这样,你就可以在Data()函数中返回data字段的指针,而不必修改其他代码。同时,json.Unmarshal()函数也能正确地识别并解析数据类型。

英文:
type IDataContainer interface {
	Data() interface{}
	SetData(newData interface{})
}
type DataA struct {
	Name string `json:"name"`
}
type StructA struct {
	data DataA
}

func (a *StructA) Data() interface{} {
	return a.data
}
func (a *StructA) SetData(newData interface{}) {
	a.data = newData.(DataA)
}

type DataB struct {
	Age int `json:"age"`
}
type StructB struct {
	data DataB
}

func (b *StructB) Data() interface{} {
	return b.data
}
func (b *StructB) SetData(newData interface{}) {
	b.data = newData.(DataB)
}

func main() {
	jsonDatas := [][]byte{[]byte(`{"name":"NEW"}`), []byte(`{"age":30}`)}
	dataContainers := []IDataContainer{&StructA{}, &StructB{}}
	for i, jsonData := range jsonDatas {
		dataContainer := dataContainers[i]
		data := dataContainer.Data()
		// json.Unmarshal(jsonData, data) //Not working, data is not a pointer.
		json.Unmarshal(jsonData, &data)
		// dataContainer.SetData(data) // Not working, type of data becomes map[string]interface{}
	}
}

The code may be a little strange because I removed irrelevant code of StructA and StructB.

There are many structs like StructA and StructB, such as StructC, StructD,StructE and so on...
They have many different functions to do different things,and they all implement the interface IDataContainer.

There are many "objects" or "instances" of them, and I have a dictionary to record them.

var dic map[string]IDataContainer = map[string]IDataContainer{...}

Users post key and json data, and I pick the IDataContainer by the key to receive the data.

Because I can't make the struct of the field "data" fixed and same,I have to use interface{} to hold the data that returned by Data().

I know that interface in golang is pointer acutally, but json.Unmarshal doesn't treat interface as pointer.

If I use "&data" as the parameter, json.Unmarshal treat it as map[string]interface{}.

I tried the code below:

type IDataContainer interface {
	Data() interface{}
	SetData(newData interface{})
}
type DataA struct {
	Name string `json:"name"`
}
type StructA struct {
	data DataA
}

func (a *StructA) Data() interface{} {
	return &a.data
}
func (a *StructA) SetData(newData interface{}) {
	a.data = *newData.(*DataA)
}

type DataB struct {
	Age int `json:"age"`
}
type StructB struct {
	data DataB
}

func (b *StructB) Data() interface{} {
	return &b.data
}
func (b *StructB) SetData(newData interface{}) {
	b.data = *newData.(*DataB)
}

func main() {
	jsonDatas := [][]byte{[]byte(`{"name":"NEW"}`), []byte(`{"age":30}`)}
	dataContainers := []IDataContainer{&StructA{}, &StructB{}}
	for i, jsonData := range jsonDatas {
		dataContainer := dataContainers[i]
		data := dataContainer.Data()
		json.Unmarshal(jsonData, &data)
		// dataContainer.SetData(data) // Not be needed,because the data is  modified by json.Unmarshal already.
	}
	fmt.Println(dataContainers[0], dataContainers[1]) // Works! but...
}

The Data() function returns the pointer of data,and json.Unmarshal detect the type conrrectly.

Althogh it works, I don't want Data() to return the pointer of the field data because of some complex reason.

How to deal with this problem?

答案1

得分: 1

好的,以下是翻译好的内容:

好的。
我使用反射(reflect)自己解决了这个问题,但是代码不够简洁。

老实说,我并不完全理解它为什么能工作。

我使用反射创建了一个临时变量,并将其类型设置为与数据的具体类型相同,然后使用它进行解组(unmarshal)。

func main() {
	jsonDatas := [][]byte{[]byte(`{"name":"NEW"}`), []byte(`{"age":30}`)}
	dataContainers := []IDataContainer{&StructA{}, &StructB{}}
	for i, jsonData := range jsonDatas {
		dataContainer := dataContainers[i]
		data := dataContainer.Data()
		t := reflect.New(reflect.TypeOf(data)).Interface()
		json.Unmarshal(jsonData, &t)
		dataContainer.SetData(reflect.ValueOf(t).Elem().Interface())
	}
	fmt.Println(dataContainers[0], dataContainers[1])
}
英文:

Ok.
I solve the problem by myself using reflect, but it is not concise.

To be honest, I can't understand completely why it works.

I make a temporary variable using reflect,and make its type same as the
concrete type of data, and unmarshal with it.

func main() {
	jsonDatas := [][]byte{[]byte(`{"name":"NEW"}`), []byte(`{"age":30}`)}
	dataContainers := []IDataContainer{&StructA{}, &StructB{}}
	for i, jsonData := range jsonDatas {
		dataContainer := dataContainers[i]
		data := dataContainer.Data()
		t := reflect.New(reflect.TypeOf(data)).Interface()
		json.Unmarshal(jsonData, &t)
		dataContainer.SetData(reflect.ValueOf(t).Elem().Interface())
	}
	fmt.Println(dataContainers[0], dataContainers[1])
}

huangapple
  • 本文由 发表于 2023年6月15日 06:02:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/76477863.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定