// 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 converter import ( "reflect" "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" ) // StructsOption is the option for Structs function. type StructsOption struct { SliceOption SliceOption StructOption StructOption } func (c *Converter) getStructsOption(option ...StructsOption) StructsOption { if len(option) > 0 { return option[0] } return StructsOption{} } // Structs converts any slice to given struct slice. // // It automatically checks and converts json string to []map if `params` is string/[]byte. // // The parameter `pointer` should be type of pointer to slice of struct. // Note that if `pointer` is a pointer to another pointer of type of slice of struct, // it will create the struct/pointer internally. func (c *Converter) Structs(params any, pointer any, option ...StructsOption) (err error) { defer func() { // Catch the panic, especially the reflection operation panics. if exception := recover(); exception != nil { if v, ok := exception.(error); ok && gerror.HasStack(v) { err = v } else { err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception) } } }() // Pointer type check. pointerRv, ok := pointer.(reflect.Value) if !ok { pointerRv = reflect.ValueOf(pointer) if kind := pointerRv.Kind(); kind != reflect.Pointer { return gerror.NewCodef( gcode.CodeInvalidParameter, "pointer should be type of pointer, but got: %v", kind, ) } } // Converting `params` to map slice. var ( paramsList []any paramsRv = reflect.ValueOf(params) paramsKind = paramsRv.Kind() structsOption = c.getStructsOption(option...) ) for paramsKind == reflect.Pointer { paramsRv = paramsRv.Elem() paramsKind = paramsRv.Kind() } switch paramsKind { case reflect.Slice, reflect.Array: paramsList = make([]any, paramsRv.Len()) for i := 0; i < paramsRv.Len(); i++ { paramsList[i] = paramsRv.Index(i).Interface() } default: paramsMaps, err := c.SliceMap(params, SliceMapOption{ SliceOption: structsOption.SliceOption, MapOption: MapOption{ ContinueOnError: structsOption.StructOption.ContinueOnError, }, }) if err != nil { return err } paramsList = make([]any, len(paramsMaps)) for i := 0; i < len(paramsMaps); i++ { paramsList[i] = paramsMaps[i] } } // If `params` is an empty slice, no conversion. if len(paramsList) == 0 { return nil } var ( reflectElemArray = reflect.MakeSlice(pointerRv.Type().Elem(), len(paramsList), len(paramsList)) itemType = reflectElemArray.Index(0).Type() itemTypeKind = itemType.Kind() pointerRvElem = pointerRv.Elem() pointerRvLength = pointerRvElem.Len() ) if itemTypeKind == reflect.Pointer { // Pointer element. for i := 0; i < len(paramsList); i++ { var tempReflectValue reflect.Value if i < pointerRvLength { // Might be nil. tempReflectValue = pointerRvElem.Index(i).Elem() } if !tempReflectValue.IsValid() { tempReflectValue = reflect.New(itemType.Elem()).Elem() } if err = c.Struct(paramsList[i], tempReflectValue, structsOption.StructOption); err != nil { return err } reflectElemArray.Index(i).Set(tempReflectValue.Addr()) } } else { // Struct element. for i := 0; i < len(paramsList); i++ { var tempReflectValue reflect.Value if i < pointerRvLength { tempReflectValue = pointerRvElem.Index(i) } else { tempReflectValue = reflect.New(itemType).Elem() } if err = c.Struct(paramsList[i], tempReflectValue, structsOption.StructOption); err != nil { return err } reflectElemArray.Index(i).Set(tempReflectValue) } } pointerRv.Elem().Set(reflectElemArray) return nil }