mirror of
https://github.com/jefferyjob/go-easy-utils.git
synced 2026-04-23 00:07:09 +08:00
134 lines
3.6 KiB
Go
134 lines
3.6 KiB
Go
package jsonxbeta
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
)
|
|
|
|
// ToStruct JSON转结构体
|
|
func ToStruct(jsonStr string, target any) error {
|
|
// 将 JSON 解成 map[string]any
|
|
var raw map[string]any
|
|
if err := json.Unmarshal([]byte(jsonStr), &raw); err != nil {
|
|
return err
|
|
}
|
|
|
|
// 判断必须是指针
|
|
rv := reflect.ValueOf(target)
|
|
if rv.Kind() != reflect.Ptr || rv.Elem().Kind() != reflect.Struct {
|
|
return ErrPoint
|
|
}
|
|
|
|
// 递归赋值到结构体中
|
|
return fill(raw, reflect.ValueOf(target).Elem(), "")
|
|
}
|
|
|
|
// fill 递归填充结构体字段
|
|
func fill(data map[string]any, target reflect.Value, path string) error {
|
|
targetType := target.Type()
|
|
|
|
for i := 0; i < target.NumField(); i++ {
|
|
field := target.Field(i)
|
|
// 若字段不可写,则跳过
|
|
if !field.CanSet() {
|
|
continue
|
|
}
|
|
|
|
structField := targetType.Field(i)
|
|
jsonTag := structField.Tag.Get("json")
|
|
|
|
// 如果 jsonTag 为空或为 "-",则使用字段名
|
|
// if jsonTag == "" || jsonTag == "-" {
|
|
// jsonTag = structField.Name
|
|
// }
|
|
|
|
// 取对应的值
|
|
value, ok := data[jsonTag]
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
// 构造字段的完整路径,用于错误提示追踪。
|
|
// 例如当前字段 jsonTag 为 "city",上一级 path 为 "address"
|
|
// 那么 newPath 就是 "address.city",表示这是嵌套结构 address 里的 city 字段。
|
|
// 如果是顶层字段(如 "name"),path 为 "",则直接使用 jsonTag 作为路径。
|
|
newPath := jsonTag
|
|
if path != "" {
|
|
newPath = path + "." + jsonTag
|
|
}
|
|
|
|
err := setFieldValue(field, structField.Type, value, newPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// setFieldValue 根据字段类型处理普通值或指针值
|
|
func setFieldValue(field reflect.Value, fieldType reflect.Type, value any, path string) error {
|
|
// 如果是指针类型,则初始化并递归赋值其 Elem()
|
|
if fieldType.Kind() == reflect.Ptr {
|
|
// 创建指针指向的类型实例
|
|
elemType := fieldType.Elem()
|
|
elemValue := reflect.New(elemType).Elem()
|
|
// 递归设置值
|
|
if err := setFieldValue(elemValue, elemType, value, path); err != nil {
|
|
return err
|
|
}
|
|
// 设置指针值
|
|
field.Set(elemValue.Addr())
|
|
return nil
|
|
}
|
|
|
|
// 非指针类型直接处理
|
|
return assignValue(field, value, path)
|
|
}
|
|
|
|
// assignValue 实际执行字段值赋值的逻辑
|
|
func assignValue(field reflect.Value, value any, path string) error {
|
|
switch field.Kind() {
|
|
case reflect.String:
|
|
field.SetString(fmt.Sprintf("%v", value))
|
|
case reflect.Bool:
|
|
return setBool(field, value, path)
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
return setInt(field, value, path)
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
return setUint(field, value, path)
|
|
case reflect.Float32, reflect.Float64:
|
|
return setFloat(field, value, path)
|
|
case reflect.Slice:
|
|
return setSlice(field, value, path)
|
|
case reflect.Map:
|
|
return setMap(field, value, path)
|
|
case reflect.Struct:
|
|
subMap, ok := value.(map[string]any)
|
|
if !ok {
|
|
// 可能是 json.RawMessage 类型,要再解一层
|
|
bs, err := json.Marshal(value)
|
|
if err != nil {
|
|
return fmt.Errorf("%s: %w", path, err)
|
|
}
|
|
var m map[string]any
|
|
if err := json.Unmarshal(bs, &m); err != nil {
|
|
return fmt.Errorf("%s: %w", path, err)
|
|
}
|
|
subMap = m
|
|
}
|
|
return fill(subMap, field, path)
|
|
case reflect.Interface:
|
|
if value == nil {
|
|
field.Set(reflect.Zero(field.Type()))
|
|
} else {
|
|
// 如果是 interface 类型,直接设置值
|
|
field.Set(reflect.ValueOf(value))
|
|
}
|
|
default:
|
|
return fmt.Errorf("%s: 不支持类型 %s", path, field.Kind())
|
|
}
|
|
|
|
return nil
|
|
}
|