
Map工具类
map获取key的val
- 如果 key 不存在于 map 中,返回默认值
- 如果 key 存在,但对应的 值是 零值 返回默认值
go
// Package maputil 提供从 map[string]interface{} 安全提取各种类型值的工具函数。
package maputil
import (
"math"
"strconv"
"strings"
)
// GetStringFromMap 从 map 中获取 string 值。
// 如果 key 不存在或类型不匹配,返回默认值(如果提供),否则返回空字符串。
func GetStringFromMap(m map[string]interface{}, key string, defaultValue ...string) string {
if m == nil {
if len(defaultValue) > 0 {
return defaultValue[0]
}
return ""
}
v, exists := m[key]
if !exists {
if len(defaultValue) > 0 {
return defaultValue[0]
}
return ""
}
if str, ok := v.(string); ok {
return str
}
if len(defaultValue) > 0 {
return defaultValue[0]
}
return ""
}
// GetBoolFromMap 从 map 中获取 bool 值。
// 支持 bool、string("true"/"false")、整数(非零为 true)等类型。
func GetBoolFromMap(m map[string]interface{}, key string, defaultValue ...bool) bool {
if m == nil {
if len(defaultValue) > 0 {
return defaultValue[0]
}
return false
}
v, exists := m[key]
if !exists {
if len(defaultValue) > 0 {
return defaultValue[0]
}
return false
}
switch val := v.(type) {
case bool:
return val
case string:
lower := strings.ToLower(strings.TrimSpace(val))
if lower == "true" {
return true
} else if lower == "false" {
return false
}
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
return val != 0
case float32, float64:
return val != 0
}
if len(defaultValue) > 0 {
return defaultValue[0]
}
return false
}
// GetInt32FromMap 从 map 中获取 int32 值。
func GetInt32FromMap(m map[string]interface{}, key string, defaultValue ...int32) int32 {
if m == nil {
if len(defaultValue) > 0 {
return defaultValue[0]
}
return 0
}
v, exists := m[key]
if !exists {
if len(defaultValue) > 0 {
return defaultValue[0]
}
return 0
}
switch val := v.(type) {
case int32:
return val
case int:
if val >= math.MinInt32 && val <= math.MaxInt32 {
return int32(val)
}
case int64:
if val >= math.MinInt32 && val <= math.MaxInt32 {
return int32(val)
}
case float64:
if val == float64(int32(val)) && val >= math.MinInt32 && val <= math.MaxInt32 {
return int32(val)
}
case string:
if i, err := strconv.ParseInt(val, 10, 32); err == nil {
return int32(i)
}
}
if len(defaultValue) > 0 {
return defaultValue[0]
}
return 0
}
func GetInt64FromMap(m map[string]interface{}, key string, defaultValue ...int64) int64 {
if m == nil {
if len(defaultValue) > 0 {
return defaultValue[0]
}
return 0
}
v, exists := m[key]
if !exists {
if len(defaultValue) > 0 {
return defaultValue[0]
}
return 0
}
switch val := v.(type) {
case int64:
return val
case int:
return int64(val)
case int32:
return int64(val)
case int16:
return int64(val)
case int8:
return int64(val)
case uint:
if val <= math.MaxInt64 {
return int64(val)
}
case uint64:
if val <= math.MaxInt64 {
return int64(val)
}
case float64:
if val == float64(int64(val)) && val >= math.MinInt64 && val <= math.MaxInt64 {
return int64(val)
}
case string:
if i, err := strconv.ParseInt(val, 10, 64); err == nil {
return i
}
default:
// 无法识别的类型,走默认值或零值
}
if len(defaultValue) > 0 {
return defaultValue[0]
}
return 0
}
// GetFloat64FromMap 从 map 中获取 float64 值。
// GetFloat64FromMap 从 map 中安全获取 float64 值。
// 支持 float64、float32、各类整数、字符串(可解析为浮点数)。
func GetFloat64FromMap(m map[string]interface{}, key string, defaultValue ...float64) float64 {
if m == nil {
if len(defaultValue) > 0 {
return defaultValue[0]
}
return 0.0
}
v, exists := m[key]
if !exists {
if len(defaultValue) > 0 {
return defaultValue[0]
}
return 0.0
}
switch val := v.(type) {
case float64:
return val
case float32:
return float64(val)
case int:
return float64(val)
case int8:
return float64(val)
case int16:
return float64(val)
case int32:
return float64(val)
case int64:
return float64(val)
case uint:
return float64(val)
case uint8:
return float64(val)
case uint16:
return float64(val)
case uint32:
return float64(val)
case uint64:
return float64(val)
case string:
if f, err := strconv.ParseFloat(val, 64); err == nil {
return f
}
// 字符串无法解析,视为无效
default:
// 不支持的类型(如 bool、map、slice 等)
}
// 类型不匹配或解析失败,返回默认值或零值
if len(defaultValue) > 0 {
return defaultValue[0]
}
return 0.0
}
// GetStringSliceFromMap 从 map 中获取 []string。
// 支持 []string 和 []interface{}(常见于 JSON)。
func GetStringSliceFromMap(m map[string]interface{}, key string, defaultValue ...[]string) []string {
if m == nil {
if len(defaultValue) > 0 {
return defaultValue[0]
}
return nil
}
v, exists := m[key]
if !exists {
if len(defaultValue) > 0 {
return defaultValue[0]
}
return nil
}
if slice, ok := v.([]string); ok {
return slice
}
if ifaceSlice, ok := v.([]interface{}); ok {
result := make([]string, len(ifaceSlice))
for i, item := range ifaceSlice {
if str, ok := item.(string); ok {
result[i] = str
} else {
// 元素非 string,整个视为无效
if len(defaultValue) > 0 {
return defaultValue[0]
}
return nil
}
}
return result
}
if len(defaultValue) > 0 {
return defaultValue[0]
}
return nil
}
// GetMapFromMap 从 map 中获取嵌套的 map[string]interface{}。
func GetMapFromMap(m map[string]interface{}, key string, defaultValue ...map[string]interface{}) map[string]interface{} {
if m == nil {
if len(defaultValue) > 0 {
return defaultValue[0]
}
return nil
}
v, exists := m[key]
if !exists {
if len(defaultValue) > 0 {
return defaultValue[0]
}
return nil
}
if nested, ok := v.(map[string]interface{}); ok {
return nested
}
if len(defaultValue) > 0 {
return defaultValue[0]
}
return nil
}测试
go
package main
import (
"demo/utils/maputil"
"fmt"
)
func main() {
data := map[string]interface{}{
"name": "Alice",
"age": "25",
"active": "true",
"tags": []interface{}{"go", "golang"},
"config": map[string]interface{}{"debug": true},
}
name := maputil.GetStringFromMap(data, "name")
age := maputil.GetInt64FromMap(data, "age")
active := maputil.GetBoolFromMap(data, "active")
tags := maputil.GetStringSliceFromMap(data, "tags")
config := maputil.GetMapFromMap(data, "config")
// 带默认值
email := maputil.GetStringFromMap(data, "email", "unknown@example.com")
retry := maputil.GetInt32FromMap(data, "retry", 3)
// 打印结果
fmt.Println("name:", name)
fmt.Println("age:", age)
fmt.Println("active:", active)
fmt.Println("tags:", tags)
fmt.Println("config:", config)
fmt.Println("email (with default):", email)
fmt.Println("retry (with default):", retry)
}StructToMap
会跳过零值
注意,即便是字段是指针类型,val也是值,所以不用担心
go
package bean_cover_util
import (
"reflect"
"strings"
)
// resolveValue 解引用指针,直到非指针类型。
// 如果遇到 nil 指针,返回一个 IsValid() == false 的 Value。
func resolveValue(v reflect.Value) reflect.Value {
for v.Kind() == reflect.Ptr {
if v.IsNil() {
return reflect.Value{} // invalid value
}
v = v.Elem()
}
return v
}
// isEmptyValue 判断一个 reflect.Value 是否为空值(zero value)
func isEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.String:
return v.String() == ""
case reflect.Map, reflect.Slice, reflect.Array:
return v.Len() == 0
case reflect.Struct:
// struct 只有所有字段都为空才算空?通常不认为 struct 是“空”,除非你有特殊逻辑
// 这里我们保守处理:struct 不算空(因为可能包含有效嵌套数据)
return false
case reflect.Interface, reflect.Ptr:
// 已经被 resolveValue 处理过,理论上不会到这里
return v.IsNil()
default:
// chan, func 等,视为非空或不支持
return false
}
}
// StructToMap 将结构体转换为 map[string]interface{}。
//
// 参数:
// - v: 要转换的结构体(值或指针)
// - omitEmpty (可选): 是否忽略空值字段(zero value),默认 false(保留所有字段)
//
// 支持:
// - 指针或值类型的 struct
// - json tag 解析(忽略 `,omitempty` 等修饰)
// - 嵌套 struct 转 map
// - struct slice 转 []map[string]interface{}
// - 指针字段自动解引用(如 *string, *User)
// - 可选忽略空值字段(通过 omitEmpty 参数)
//
// 规则:
// - 忽略 json:"-" 或无 json tag 的字段(⚠️ 注意:这里“无 tag”仍会使用字段名,见下)
// 实际上:无 json tag 时使用小驼峰字段名,不会被忽略
// - 若 tag 包含 ,(如 "name,omitempty"),仅取前面部分
// - 若 tag 为空,则使用小驼峰字段名(fieldName)
// - 非导出字段(小写开头)不会被反射访问
func StructToMap(v interface{}, omitEmpty ...bool) map[string]interface{} {
doOmit := len(omitEmpty) > 0 && omitEmpty[0]
m := make(map[string]interface{})
rv := reflect.ValueOf(v)
// 解引用顶层指针
if rv.Kind() == reflect.Ptr {
if rv.IsNil() {
return m
}
rv = rv.Elem()
}
if rv.Kind() != reflect.Struct {
return m
}
rt := rv.Type()
for i := 0; i < rv.NumField(); i++ {
field := rt.Field(i)
value := rv.Field(i)
// 获取 json tag
tag := field.Tag.Get("json")
if tag == "-" {
continue
}
// 去除 tag 中的选项(如 ",omitempty")
if idx := strings.Index(tag, ","); idx != -1 {
tag = tag[:idx]
}
key := tag
if key == "" {
// 使用小驼峰命名
key = strings.ToLower(field.Name[:1]) + field.Name[1:]
}
// 解引用字段值(处理 *T, **T 等)
actualValue := resolveValue(value)
// 如果字段是指针且为 nil
if !actualValue.IsValid() {
if doOmit {
continue // 忽略 nil 字段
}
m[key] = nil
continue
}
// 决定最终要存入 map 的值
var finalValue interface{}
switch actualValue.Kind() {
case reflect.Struct:
nestedMap := StructToMap(actualValue.Interface(), doOmit)
if doOmit && len(nestedMap) == 0 {
continue // 嵌套 struct 全为空,且要求 omitEmpty,则跳过
}
finalValue = nestedMap
case reflect.Slice:
elemType := actualValue.Type().Elem()
sliceLen := actualValue.Len()
if elemType.Kind() == reflect.Struct {
sliceMap := make([]interface{}, sliceLen)
for j := 0; j < sliceLen; j++ {
item := actualValue.Index(j)
itemResolved := resolveValue(item)
if !itemResolved.IsValid() {
sliceMap[j] = nil
} else if itemResolved.Kind() == reflect.Struct {
itemMap := StructToMap(itemResolved.Interface(), doOmit)
sliceMap[j] = itemMap
if len(itemMap) > 0 {
}
} else {
sliceMap[j] = itemResolved.Interface()
if !isEmptyValue(itemResolved) {
}
}
}
// 注意:即使 slice 全空,我们也保留空 slice [],除非你希望 omit 整个字段?
// 这里策略:slice 本身不因内容为空而 omit 字段(因为 [] 也是有效状态)
finalValue = sliceMap
} else {
// 普通 slice(如 []string, []*int)
convertedSlice := make([]interface{}, sliceLen)
for j := 0; j < sliceLen; j++ {
elem := actualValue.Index(j)
elemResolved := resolveValue(elem)
if !elemResolved.IsValid() {
convertedSlice[j] = nil
} else {
convertedSlice[j] = elemResolved.Interface()
}
}
finalValue = convertedSlice
}
default:
finalValue = actualValue.Interface()
}
// 检查是否应该忽略空值
if doOmit {
// 对于非 struct/slice 类型,检查是否为 zero value
if actualValue.Kind() != reflect.Struct && actualValue.Kind() != reflect.Slice {
if isEmptyValue(actualValue) {
continue
}
}
// struct 和 slice 的 omit 逻辑已在上面处理
}
m[key] = finalValue
}
return m
}测试
go
package main
import (
"demo/utils/bean_cover_util"
"fmt"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Email *string `json:"email"`
Hobbies []string `json:"hobbies"`
Address *Address `json:"address"`
}
type Address struct {
City string `json:"city"`
Code int `json:"code"`
}
func main() {
user := User{
Name: "Alice",
// Age 为 0(空值)
// Email 为 nil
Hobbies: []string{}, // 空 slice
// Address 为 nil
}
// 保留所有字段(包括空值)
fullMap := bean_cover_util.StructToMap(user)
fmt.Println("Full map:", fullMap)
// 输出: map[address:<nil> age:0 email:<nil> hobbies:[] name:Alice]
// 忽略空值字段
compactMap := bean_cover_util.StructToMap(user, true)
fmt.Println("Compact map:", compactMap)
// 输出: map[hobbies:[] name:Alice] (其他都是空值或 nil,被忽略)
}只跳过nil值
go
package bean_cover_util
import (
"reflect"
"strings"
)
// StructToMap 将结构体转换为 map[string]interface{}。
//
// 行为:
// - 仅跳过值为 nil 的指针字段(如 *string, *User 等为 nil 时)
// - 不跳过零值(0, "", false, empty slice/map 等都会保留)
// - 非指针字段一律保留
//
// 支持:
// - 值或指针类型的 struct
// - json tag(忽略 ",xxx" 部分,支持 "-" 忽略)
// - 嵌套 struct / slice of struct
// - 自动解引用指针字段(非 nil 的)
func StructToMap(v interface{}) map[string]interface{} {
m := make(map[string]interface{})
rv := reflect.ValueOf(v)
// 解引用顶层指针(支持传入 *Struct)
if rv.Kind() == reflect.Ptr {
if rv.IsNil() {
return m
}
rv = rv.Elem()
}
if rv.Kind() != reflect.Struct {
return m
}
rt := rv.Type()
for i := 0; i < rv.NumField(); i++ {
field := rt.Field(i)
fieldValue := rv.Field(i)
// 跳过未导出字段(小写开头)—— reflect 无法访问,但保险起见可加判断
if !fieldValue.CanInterface() {
continue
}
// 获取 json tag
tag := field.Tag.Get("json")
if tag == "-" {
continue
}
// 去除 tag 中的选项(如 ",omitempty")
if idx := strings.Index(tag, ","); idx != -1 {
tag = tag[:idx]
}
key := tag
if key == "" {
// 使用小驼峰命名
key = strings.ToLower(field.Name[:1]) + field.Name[1:]
}
// === 关键:跳过 nil 指针字段 ===
if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() {
continue
}
// 递归处理值(自动解引用非 nil 指针)
finalValue := convertValue(fieldValue)
m[key] = finalValue
}
return m
}
// convertValue 递归转换 reflect.Value 为 interface{}
func convertValue(v reflect.Value) interface{} {
// 解引用所有指针(直到非指针)
for v.Kind() == reflect.Ptr {
if v.IsNil() {
// 理论上不会到这里,因为调用方已过滤 nil 指针
return nil
}
v = v.Elem()
}
switch v.Kind() {
case reflect.Struct:
return StructToMap(v.Interface())
case reflect.Slice:
sliceLen := v.Len()
result := make([]interface{}, sliceLen)
for i := 0; i < sliceLen; i++ {
result[i] = convertValue(v.Index(i))
}
return result
case reflect.Map:
mapLen := v.Len()
if mapLen == 0 {
// 返回空 map(保持类型信息丢失,但 interface{} 只能这样)
return map[string]interface{}{}
}
result := make(map[string]interface{})
for _, key := range v.MapKeys() {
strKey := key.String() // 注意:key 必须可转 string,否则 panic
result[strKey] = convertValue(v.MapIndex(key))
}
return result
default:
return v.Interface()
}
}测试
go
package main
import (
"demo/utils/bean_cover_util"
"fmt"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"` // 0 → 保留
Email *string `json:"email"` // nil → 跳过
Mobile *string `json:"mobile"` // 非 nil → 保留
Tags []string `json:"tags"` // [] → 保留
Address *Address `json:"address"` // nil → 跳过
}
type Address struct {
City string `json:"city"`
Code int `json:"code"`
}
func main() {
name := "Alice"
mobile := "+123"
user := User{
Name: name,
Age: 0, // 零值,但保留
Email: nil, // nil 指针,跳过
Mobile: &mobile, // 非 nil,保留
Tags: []string{}, // 空 slice,保留
// Address: nil, // nil 指针,跳过
}
m := bean_cover_util.StructToMap(user)
fmt.Printf("%+v\n", m)
}
