Files
gf/util/gconv/gconv_z_unit_scan_omit_test.go
Lance Add de9d3c2b3c feat(util/gconv): Add OmitEmpty and OmitNil options to Scan function (#4584)
## 改进内容
- 扩展 `ScanOption`/`StructOption` 结构体,添加 `OmitEmpty bool` 字段:当设置为 true
时,跳过空值(如空字符串、零值等)的赋值;添加 `OmitNil bool` 字段:当设置为 true 时,跳过 nil 值的赋值;
- 添加 `ScanWithOptions` 函数,支持通过 `ScanOption` 参数使用新选项
- 原有的 `Scan` 函数行为完全不变
- 通过 `NewConverter` 创建的转换器也支持新功能

## 使用示例

### 基本用法
```go
type User struct {
    Name  *string
    Age   int
    Email string
}

type Person struct {
    Name  string
    Age   int
    Email string
}

user := User{Name: nil, Age: 25, Email: ""}
person := Person{Name: "zhangsan", Age: 0, Email: "old@example.com"}

err := gconv.ScanWithOptions(user, &person, gconv.ScanOption{
    OmitEmpty: true,
    OmitNil: true,
})
// 结果: person.Name 保持 "zhangsan",person.Age 变为 25,person.Email 保持 "old@example.com"
```

后续可以将`func Scan(srcValue any, dstPointer any, paramKeyToAttrMap
...map[string]string) (err error)`和`func ScanWithOptions(srcValue any,
dstPointer any, option ...ScanOption) (err error)`直接用`func Scan(srcValue
any, dstPointer any, option ...ScanOption) (err
error)`代替,`ScanOption`里已经包含了`paramKeyToAttrMap map[string]string`

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-16 10:19:02 +08:00

147 lines
3.4 KiB
Go

// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gconv_test
import (
"testing"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/util/gconv"
)
type User struct {
Name string
Age int
Email string
}
type User2 struct {
Name *string
Age int
Email string
}
type Person struct {
Name string
Age int
Email string
}
func TestScan_OmitEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
user := User{Name: "", Age: 20, Email: ""}
person := Person{Name: "zhangsan", Age: 0, Email: "old@example.com"}
err := gconv.ScanWithOptions(user, &person, gconv.ScanOption{
OmitEmpty: true,
})
t.AssertNil(err)
t.Assert(person.Name, "zhangsan")
t.Assert(person.Age, 20)
t.Assert(person.Email, "old@example.com")
})
}
func TestScan_AllOmitEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
user := User{Name: "", Age: 0, Email: ""}
person := Person{Name: "zhangsan", Age: 100, Email: "old@example.com"}
err := gconv.ScanWithOptions(user, &person, gconv.ScanOption{
OmitEmpty: true,
})
t.AssertNil(err)
t.Assert(person.Name, "zhangsan")
t.Assert(person.Age, 100)
t.Assert(person.Email, "old@example.com")
})
}
func TestScan_OmitNil(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
data := map[string]any{
"Name": nil,
"Age": 30,
"Email": nil,
}
person := Person{Name: "lisi", Age: 0, Email: "old@example.com"}
err := gconv.ScanWithOptions(data, &person, gconv.ScanOption{
OmitNil: true,
})
t.AssertNil(err)
t.Assert(person.Name, "lisi")
t.Assert(person.Age, 30)
t.Assert(person.Email, "old@example.com")
})
}
func TestScan_OmitEmptyAndOmitNil(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
data := map[string]any{
"Name": "",
"Age": 25,
"Email": nil,
}
person := Person{Name: "wangwu", Age: 0, Email: "old2@example.com"}
err := gconv.ScanWithOptions(data, &person, gconv.ScanOption{
OmitEmpty: true,
OmitNil: true,
})
t.AssertNil(err)
t.Assert(person.Name, "wangwu")
t.Assert(person.Age, 25)
t.Assert(person.Email, "old2@example.com")
})
}
func TestScan_NoOmitOptions(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
user := User{Name: "", Age: 20, Email: ""}
person := Person{Name: "zhangsan", Age: 30, Email: "old@example.com"}
err := gconv.ScanWithOptions(user, &person, gconv.ScanOption{
OmitEmpty: false,
OmitNil: false,
})
t.AssertNil(err)
t.Assert(person.Name, "")
t.Assert(person.Age, 20)
t.Assert(person.Email, "")
})
}
func TestScan_OriginalBehavior(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
user := User{Name: "newname", Age: 25, Email: "new@example.com"}
person := Person{Name: "", Age: 0, Email: ""}
err := gconv.Scan(user, &person)
t.AssertNil(err)
t.Assert(person.Name, "newname")
t.Assert(person.Age, 25)
t.Assert(person.Email, "new@example.com")
})
}
func TestScan_StructOmitEmptyAndOmitNilOptions(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
user2 := User2{Name: nil, Age: 25, Email: ""}
person := Person{Name: "wangwu", Age: 0, Email: "old2@example.com"}
err := gconv.ScanWithOptions(user2, &person, gconv.ScanOption{
OmitEmpty: true,
OmitNil: true,
})
t.AssertNil(err)
t.Assert(person.Name, "wangwu")
t.Assert(person.Age, 25)
t.Assert(person.Email, "old2@example.com")
})
}