Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In a nested structure, when there is an external attribute with the same name as the internal structure, gconv.Scan will be affected by the position order of the nested structure and the result will be unstable. #3800

Closed
alaywn opened this issue Sep 24, 2024 · 0 comments
Assignees
Labels
bug It is confirmed a bug, but don't worry, we'll handle it. done This issue is done, which may be release in next version.

Comments

@alaywn
Copy link
Contributor

alaywn commented Sep 24, 2024

Go version

go version go1.23.1 darwin/arm64

GoFrame version

2.7.3

Can this bug be reproduced with the latest release?

Option Yes

What did you do?

如下是单测:

package test

import (
	"fmt"
	"testing"

	"github.com/gogf/gf/v2/util/gconv"
)

type NullID string

type StructA struct {
	Superior    string `json:"superior"`
	UpdatedTick int    `json:"updated_tick"`
}
type StructB struct {
	Superior    *NullID `json:"superior"`
	UpdatedTick *int    `json:"updated_tick"`
}

type StructC struct {
	Superior    string `json:"superior"`
	UpdatedTick int    `json:"updated_tick"`
}
type StructD struct {
	StructC
	Superior    *NullID `json:"superior"`
	UpdatedTick *int    `json:"updated_tick"`
}

type StructE struct {
	Superior    string `json:"superior"`
	UpdatedTick int    `json:"updated_tick"`
}
type StructF struct {
	Superior    *NullID `json:"superior"`
	UpdatedTick *int    `json:"updated_tick"`
	StructE
}

type StructG struct {
	Superior    string `json:"superior"`
	UpdatedTick int    `json:"updated_tick"`
}
type StructH struct {
	Superior    *string `json:"superior"`
	UpdatedTick *int    `json:"updated_tick"`
	StructG
}

type StructI struct {
	Master struct {
		Superior    *NullID `json:"superior"`
		UpdatedTick int     `json:"updated_tick"`
	} `json:"master"`
}
type StructJ struct {
	StructA
	Superior    *NullID `json:"superior"`
	UpdatedTick *int    `json:"updated_tick"`
}

type StructK struct {
	Master struct {
		Superior    *NullID `json:"superior"`
		UpdatedTick int     `json:"updated_tick"`
	} `json:"master"`
}
type StructL struct {
	Superior    *NullID `json:"superior"`
	UpdatedTick *int    `json:"updated_tick"`
	StructA
}

func TestScan(t *testing.T) {
	// 测试1:没有结构嵌套,Superior为带字母的string,正常
	fmt.Println("测试1")
	structA := StructA{
		Superior:    "superior100",
		UpdatedTick: 20,
	}
	structB := StructB{}
	if err := gconv.Scan(structA, &structB); err != nil {
		fmt.Printf("structA to structB: %s", err.Error())
	}
	fmt.Println("structB.Superior:", *structB.Superior)
	fmt.Println("structB.UpdatedTick:", *structB.UpdatedTick)

	// 测试2:没有结构嵌套,Superior为纯数字的string,正常
	fmt.Println("测试2")
	structA1 := StructA{
		Superior:    "100",
		UpdatedTick: 20,
	}
	structB1 := StructB{}
	if err := gconv.Scan(structA1, &structB1); err != nil {
		fmt.Printf("structA1 to structB1: %s", err.Error())
	}
	fmt.Println("structB1.Superior:", *structB1.Superior)
	fmt.Println("structB1.UpdatedTick:", *structB1.UpdatedTick)

	// 测试3:结构嵌套,Superior为带字母的string,正常
	fmt.Println("测试3")
	structC := StructC{
		Superior:    "superior100",
		UpdatedTick: 20,
	}
	structD := StructD{}
	if err := gconv.Scan(structC, &structD); err != nil {
		fmt.Printf("structC to structD: %s", err.Error())
	}
	fmt.Println("structD.StructC.Superior:", structD.StructC.Superior)
	fmt.Println("structD.Superior:", *structD.Superior)
	fmt.Println("structD.UpdatedTick:", *structD.UpdatedTick)

	// 测试4:结构嵌套,Superior为纯数字的string,出现 structD1.Superior 值为 nil
	fmt.Println("测试4")
	structC1 := StructC{
		Superior:    "100",
		UpdatedTick: 20,
	}
	structD1 := StructD{}
	if err := gconv.Scan(structC1, &structD1); err != nil {
		fmt.Printf("structC1 to structD1: %s", err.Error())
	}
	fmt.Println("structD1.StructC.Superior:", structD1.StructC.Superior) // structD1.StructC.Superior: 100
	fmt.Println("structD1.Superior:", structD1.Superior)                 // structD1.Superior: <nil>
	fmt.Println("structD1.UpdatedTick:", *structD1.UpdatedTick)          // structD1.UpdatedTick: 20

	// 测试5:结构嵌套,Superior为带字母的string,正常
	fmt.Println("测试5")
	structE := StructE{
		Superior:    "superior100",
		UpdatedTick: 20,
	}
	structF := StructF{}
	if err := gconv.Scan(structE, &structF); err != nil {
		fmt.Printf("structE to structF: %s", err.Error())
	}
	fmt.Println("structF.StructE.Superior:", structF.StructE.Superior)
	fmt.Println("structF.Superior:", *structF.Superior)
	fmt.Println("structF.UpdatedTick:", *structF.UpdatedTick)

	// 测试6:结构嵌套,Superior为纯数字的string,正常
	fmt.Println("测试6")
	structE1 := StructE{
		Superior:    "100",
		UpdatedTick: 20,
	}
	structF1 := StructF{}
	if err := gconv.Scan(structE1, &structF1); err != nil {
		fmt.Printf("structE1 to structF1: %s\n", err.Error()) // 报错
	}
	fmt.Println("structF1.StructE.Superior:", structF1.StructE.Superior) // structF1.StructE.Superior: 100
	fmt.Println("structF1.Superior:", *structF1.Superior)                // structF1.Superior: 100
	fmt.Println("structF1.UpdatedTick:", *structF1.UpdatedTick)          // structF1.UpdatedTick: 20

	// 测试7:结构嵌套,Superior为带字母的string,目标 Superior 类型 为 *string,正常
	fmt.Println("测试7")
	structG := StructG{
		Superior:    "superior100",
		UpdatedTick: 20,
	}
	structH := StructH{}
	if err := gconv.Scan(structG, &structH); err != nil {
		fmt.Printf("structG to structH: %s\n", err.Error()) // 报错
	}
	fmt.Println("structH.StructG.Superior:", structH.StructG.Superior) // structH.StructG.Superior: superior100
	fmt.Println("structH.Superior:", *structH.Superior)                // structH.Superior: superior100
	fmt.Println("structH.UpdatedTick:", *structH.UpdatedTick)          // structH.UpdatedTick: 20

	// 测试8:结构嵌套,Superior为纯数字的string,目标 Superior 类型 为 *string
	// 报错 error binding srcValue to attribute "Superior": json.UnmarshalUseNumber failed: json: cannot unmarshal number into Go value of type string
	fmt.Println("测试8")
	structG1 := StructG{
		Superior:    "100",
		UpdatedTick: 20,
	}
	structH1 := StructH{}
	if err := gconv.Scan(structG1, &structH1); err != nil {
		fmt.Printf("structG to structH: %s\n", err.Error()) // 报错
	}
	fmt.Println("structH1.StructG.Superior:", structH1.StructG.Superior) // structH1.StructG.Superior: ""
	fmt.Println("structH1.Superior:", *structH1.Superior)                // structH1.Superior: 100
	fmt.Println("structH1.UpdatedTick:", structH1.UpdatedTick)           // structH1.UpdatedTick: <nil>

	// 测试9:结构嵌套,structJ.Superior 值为 nil
	fmt.Println("测试9")
	structI := StructI{}
	xxx := NullID("superior100")
	structI.Master.Superior = &xxx
	structI.Master.UpdatedTick = 30
	structJ := StructJ{}
	if err := gconv.Scan(structI.Master, &structJ); err != nil {
		fmt.Printf("structI to structJ: %s\n", err.Error()) // 报错
	}
	fmt.Println("structJ.StructA.Superior:", structJ.StructA.Superior)       // structJ.StructA.Superior: superior100
	fmt.Println("structJ.Superior:", structJ.Superior)                       // structJ.Superior: <nil>
	fmt.Println("structJ.StructA.UpdatedTick:", structJ.StructA.UpdatedTick) // structJ.UpdatedTick: 30
	fmt.Println("structJ.UpdatedTick:", *structJ.UpdatedTick)                // structJ.UpdatedTick: 30

	// 测试10:结构嵌套,正常
	fmt.Println("测试10")
	structK := StructK{}
	yyy := NullID("superior100")
	structK.Master.Superior = &yyy
	structK.Master.UpdatedTick = 40
	structL := StructL{}
	if err := gconv.Scan(structK.Master, &structL); err != nil {
		fmt.Printf("structK to structL: %s\n", err.Error()) // 报错
	}
	fmt.Println("structL.StructA.Superior:", structL.StructA.Superior)       // structL.StructA.Superior: superior100
	fmt.Println("structL.Superior:", *structL.Superior)                      // structL.Superior: superior100
	fmt.Println("structL.StructA.UpdatedTick:", structL.StructA.UpdatedTick) // structJ.UpdatedTick: 30
	fmt.Println("structL.UpdatedTick:", *structL.UpdatedTick)                // structL.UpdatedTick: 30
}

What did you see happen?

参照上述单测结果

What did you expect to see?

向嵌套结构体中Scan数据时,内部不存在相同属性时,应该优先赋值外部同名属性,无论嵌套结构体的位置顺序如何

@alaywn alaywn added the bug It is confirmed a bug, but don't worry, we'll handle it. label Sep 24, 2024
@Issues-translate-bot Issues-translate-bot changed the title 【BUG】嵌套结构体中,外部存在一个跟内部结构体种同名的属性时,gconv.Scan受嵌套结构体位置顺序影响而出现结果不稳定的情况 [BUG] In a nested structure, when there is an external attribute with the same name as the internal structure, gconv.Scan will be affected by the position order of the nested structure and the result will be unstable. Sep 24, 2024
@gqcn gqcn self-assigned this Sep 24, 2024
@gqcn gqcn changed the title [BUG] In a nested structure, when there is an external attribute with the same name as the internal structure, gconv.Scan will be affected by the position order of the nested structure and the result will be unstable. In a nested structure, when there is an external attribute with the same name as the internal structure, gconv.Scan will be affected by the position order of the nested structure and the result will be unstable. Sep 24, 2024
@gqcn gqcn added the done This issue is done, which may be release in next version. label Sep 24, 2024
@gqcn gqcn closed this as completed Sep 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug It is confirmed a bug, but don't worry, we'll handle it. done This issue is done, which may be release in next version.
Projects
None yet
Development

No branches or pull requests

2 participants