场景:第三方传过来的数据是json,用json.Unmarshal()转结构体的时候int类型自动转成科学计算法,或者转成floate64类型很是头大。为此想了很多办法最后找到json.number+反射解决。

1、将拿到的字符串使用json.number处理代码如下:

decoder1 := json.NewDecoder(bytes.NewBuffer([]byte("")))
decoder1.UseNumber()
err := decoder1.Decode(结构体指针)

2、通过json.number解析处理的map[string]interface{}转换为结构体代码如下:

func MapTOStruct(data map[string]interface{}, inter interface{}) (err error) {

	var targetField *string
	defer func() {
		if p := recover(); p != nil {
			errMsg := fmt.Sprintf("MapTOStruct error:%v, field:%s", p, *targetField)
			err = errors.New(errMsg)
		}
	}()
	// 把key转成小写
	dataLower := make(map[string]interface{})
	for k, v := range data {
		dataLower[strings.ToLower(k)] = v
	}

	poValues := reflect.ValueOf(inter).Elem()
	poTypes := poValues.Type()

	for i := 0; i < poValues.NumField(); i++ {
		// po字段类型
		poField := poTypes.Field(i)
		// po字段值
		poValue := poValues.Field(i)
		// 字段名(优先用json注释,如果json注释不存在,则使用db注释)
		colName := poField.Tag.Get("json")
		if len(colName) == 0 {
			colName = poField.Tag.Get("db")
		}
		targetField = &colName
		// map值
		value, isExists := dataLower[strings.ToLower(colName)]
		var strValue = ""
		if value != nil {
			strValue = fmt.Sprintf("%v", value.(interface{}))
		}
		//根据po字段类型赋值
		switch poField.Type.String() {
		case "sql.NullString":
			if !isExists || value == nil || value == "" {
				poValue.Set(reflect.ValueOf(NullString("", true)))
			} else {
				poValue.Set(reflect.ValueOf(NullString(strValue, true)))
			}
		case "sql.NullInt32":
			if !isExists || value == nil {
				poValue.Set(reflect.ValueOf(NullInt32(0, true)))
			} else {
				intvalue, err := strconv.Atoi(strValue)
				if err != nil {
					continue
				}
				poValue.Set(reflect.ValueOf(NullInt32(int32(intvalue), true)))
			}
		case "sql.NullInt64":
			if !isExists || value == nil {
				poValue.Set(reflect.ValueOf(NullInt64(0, true)))
			} else {
				intvalue, err := strconv.ParseInt(strValue, 10, 64)
				if err != nil {
					continue
				}
				poValue.Set(reflect.ValueOf(NullInt64(intvalue, true)))
			}
		case "sql.NullFloat64":
			if !isExists || value == nil {
				poValue.Set(reflect.ValueOf(NullFloat64(0, true)))
			} else {
				float64value, err := strconv.ParseFloat(strValue, 64)
				if err != nil {
					continue
				}
				poValue.Set(reflect.ValueOf(NullFloat64(float64value, true)))
			}
		case "sql.NullTime":
			if !isExists || value == nil || len(strValue) == 0 {
				poValue.Set(reflect.ValueOf(sql.NullTime{Valid: false}))
			} else {
				poValue.Set(reflect.ValueOf(NullTime(parseTimeStr(strValue), true)))
			}
		case "time.Time":
			if !isExists || value == nil || len(strValue) == 0 {
				continue
			}
			poValue.Set(reflect.ValueOf(parseTimeStr(strValue)))
		default:
			if !isExists || value == nil {
				continue
			}
			if poField.Type.String() == "int32" {
				valueInt32, err := strconv.ParseInt(strValue, 10, 32)
				if err != nil {
					continue
				}
				poValue.Set(reflect.ValueOf(int32(valueInt32)))

			} else if poField.Type.String() == "uint32" {
				valueUint32, err := strconv.ParseInt(strValue, 10, 32)
				if err != nil {
					continue
				}
				poValue.Set(reflect.ValueOf(uint32(valueUint32)))

			} else if poField.Type.String() == "int64" {
				valueInt64, err := strconv.ParseInt(strValue, 10, 64)
				if err != nil {
					continue
				}
				poValue.Set(reflect.ValueOf(valueInt64))

			} else if poField.Type.String() == "uint64" {
				valueUint64, err := strconv.ParseUint(strValue, 10, 64)
				if err != nil {
					continue
				}
				poValue.Set(reflect.ValueOf(valueUint64))

			} else if poField.Type.String() == "float64" {
				valueFloat64, err := strconv.ParseFloat(strValue, 64)
				if err != nil {
					continue
				}
				poValue.Set(reflect.ValueOf(valueFloat64))

			} else if poField.Type.String() == "string" {
				poValue.Set(reflect.ValueOf(strValue))
			} else if poField.Type.String() == "bool" {
				valueBool, err := strconv.ParseBool(strValue)
				if err != nil {
					continue
				}
				poValue.Set(reflect.ValueOf(valueBool))

			} else {
				poValue.Set(reflect.ValueOf(value))
			}
		}
	}
	return nil
}

var TimeLayout = []string{
	"2006-01-02 15:04:05.000",
}

func parseTimeStr(timeStr string) time.Time {

	for i := 0; i < len(TimeLayout); i++ {
		layout := TimeLayout[i]
		var err error
		var result time.Time
		if strings.Contains(layout, "Z") || strings.Contains(layout, "z") {
			result, err = time.Parse(layout, timeStr)
		} else {
			result, err = time.ParseInLocation(layout, timeStr, time.Local)
		}
		if err == nil {
			return result
		}
	}
	return time.Time{}
}

func NullString(str string, valid bool) sql.NullString {
	return sql.NullString{String: str, Valid: valid}
}

func NullInt32(i int32, valid bool) sql.NullInt32 {
	return sql.NullInt32{Int32: i, Valid: valid}
}

func NullInt64(i int64, valid bool) sql.NullInt64 {
	return sql.NullInt64{Int64: i, Valid: valid}
}

func NullFloat64(f float64, valid bool) sql.NullFloat64 {
	return sql.NullFloat64{Float64: f, Valid: valid}
}

func NullTime(t time.Time, valid bool) sql.NullTime {
	return sql.NullTime{Time: t, Valid: valid}
}