Golang 通过jsonTag设置Struct或者Struct数组中的字段

本文介绍了一种通过JSON标签来设定或更新结构体字段的方法,包括处理指针、切片和数组类型的对象,并提供了详细的Go语言实现代码及测试用例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文地址声明:https://blog.csdn.net/qq_23179075/article/details/118394526

package common

import (
    "fmt"
    "reflect"
    "strings"
)

// SetValueFromJsonTag 通过jsonTag设置 src 对象中的字段 , 支持类型: struct, slice
func SetValueFromJsonTag(src interface{}, jsonTag string, value interface{}) error {
	valueOf := reflect.ValueOf(src)
	kind := valueOf.Kind()
	if kind == reflect.Ptr {
		valueOf = valueOf.Elem()
		kind = valueOf.Kind()
	}

	if kind == reflect.Struct {
		return setStructValue(valueOf, jsonTag, value)
	}

	if kind == reflect.Slice || kind == reflect.Array {
		return setSliceValue(valueOf, jsonTag, value)
	}
	return fmt.Errorf(`only struct|slice|array types are supported`)
}

func setStructValue(valueOf reflect.Value, jsonTag string, value interface{}) (err error) {
	defer func() {
		if rerr := recover(); rerr != nil {
			err = fmt.Errorf("%v", rerr)
		}
	}()

	vOf := reflect.ValueOf(value)
	if vOf.Kind() == reflect.Ptr {
		vOf = vOf.Elem()
	}

	for i := 0; i < valueOf.NumField(); i++ {
		typeField := valueOf.Type().Field(i)
		valueField := valueOf.Field(i)
		//如果字段是匿名对象
		if typeField.Anonymous {
			if err = setStructValue(valueField, jsonTag, value); err == nil {
				return nil
			}
		}

		if getJsonTag(typeField) == jsonTag {
			vu, err := getNewValue(valueField, vOf, jsonTag)
			if err != nil {
				return err
			}
			valueField.Set(vu)
			return nil
		}
	}
	return fmt.Errorf(`object does not have the json:"%v"`, jsonTag)
}

func setSliceValue(valueOf reflect.Value, jsonTag string, value interface{}) (err error) {
	defer func() {
		if rerr := recover(); rerr != nil {
			err = fmt.Errorf("%v", rerr)
		}
	}()

	vOf := reflect.ValueOf(value)
	if vOf.Kind() == reflect.Ptr {
		vOf = vOf.Elem()
	}

	for i := 0; i < valueOf.Len(); i++ {
		item := valueOf.Index(i)
		if item.Kind() == reflect.Ptr {
			item = item.Elem()
		}
		if item.Kind() != reflect.Struct {
			continue
		}

		if err = setStructValue(item, jsonTag, value); err != nil {
			return err
		}
	}
	return nil
}

func getNewValue(valueField reflect.Value, vOf reflect.Value, jsonTag string) (reflect.Value, error) {
	if valueField.Kind() != vOf.Kind() {
		if vOf.Kind() != reflect.Float64 {
			return reflect.Value{}, fmt.Errorf(`value of type %v is not assignable to type json:"%v"->%v`, vOf.Kind(), jsonTag, valueField.Kind())
		}
		ivOf := vOf.Interface().(float64)
		switch valueField.Kind() {
		case reflect.Int:
			return reflect.ValueOf(int(ivOf)), nil
		case reflect.Int32:
			return reflect.ValueOf(int32(ivOf)), nil
		case reflect.Int64:
			return reflect.ValueOf(int64(ivOf)), nil
		case reflect.Float32:
			return reflect.ValueOf(float32(ivOf)), nil
		default:
			return reflect.Value{}, fmt.Errorf(`value of type %v is not assignable to type json:"%v"->%v`, vOf.Kind(), jsonTag, valueField.Kind())
		}
	}
	return vOf, nil
}

func getJsonTag(structField reflect.StructField) string {
	fieldName := structField.Name
	tag := structField.Tag.Get("json")

	//fmt.Println("structField.Name:", fieldName, "; jsonTag:", tag,"; anonymous:",anonymous)

	if tag == "" {
		return fieldName
	}
	if strings.Contains(tag, ",") {
		split := strings.Split(tag, ",")
		if len(split) > 0 {
			s := split[0]
			if s == "" {
				return fieldName
			}
			return s
		}
	}
	return tag
}

测试用例

package common

import (
    "fmt"
    "testing"
)

type Form struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}


func TestFrom(t *testing.T) {
    form := Form{Name: "张飞", Age: 24}

    if err := SetValueFromJsonTag(&form, "name", "关羽"); err != nil {
        fmt.Println(err)
    }
    fmt.Println(form)

    if err := SetValueFromJsonTag(&form, "age", 32); err != nil {
        fmt.Println(err)
    }
    fmt.Println(form)

    if err := SetValueFromJsonTag(&form, "name", 32); err != nil {
        fmt.Println(err)
    }
    fmt.Println(form)

    if err := SetValueFromJsonTag(&form, "age", "关羽"); err != nil {
        fmt.Println(err)
    }
    fmt.Println(form)

    if err := SetValueFromJsonTag(&form, "sex", true); err != nil {
        fmt.Println(err)
    }
    fmt.Println(form)

    if err := SetValueFromJsonTag(form, "age", 48); err != nil {
        fmt.Println(err)
    }
    fmt.Println(form)

}

func TestFroms(t *testing.T) {
    forms := []Form{
        {"刘备", 32},
        {"张飞", 24},
        {"关羽", 26},
    }

    if err := SetValueFromJsonTag(forms, "name", "赵云"); err != nil {
        fmt.Println(err)
    }
    fmt.Println(forms)

    if err := SetValueFromJsonTag(forms, "age", 32); err != nil {
        fmt.Println(err)
    }
    fmt.Println(forms)

    if err := SetValueFromJsonTag(forms, "name", 32); err != nil {
        fmt.Println(err)
    }
    fmt.Println(forms)

    if err := SetValueFromJsonTag(forms, "age", "关羽"); err != nil {
        fmt.Println(err)
    }
    fmt.Println(forms)

    if err := SetValueFromJsonTag(forms, "sex", true); err != nil {
        fmt.Println(err)
    }
    fmt.Println(forms)

    if err := SetValueFromJsonTag(&forms, "age", 48); err != nil {
        fmt.Println(err)
    }
    fmt.Println(forms)
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值