Path: blob/main/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go
2880 views
// Package mapstructure exposes functionality to convert one arbitrary1// Go type into another, typically to convert a map[string]any2// into a native Go structure.3//4// The Go structure can be arbitrarily complex, containing slices,5// other structs, etc. and the decoder will properly decode nested6// maps and so on into the proper structures in the native Go struct.7// See the examples to see what the decoder is capable of.8//9// The simplest function to start with is Decode.10//11// # Field Tags12//13// When decoding to a struct, mapstructure will use the field name by14// default to perform the mapping. For example, if a struct has a field15// "Username" then mapstructure will look for a key in the source value16// of "username" (case insensitive).17//18// type User struct {19// Username string20// }21//22// You can change the behavior of mapstructure by using struct tags.23// The default struct tag that mapstructure looks for is "mapstructure"24// but you can customize it using DecoderConfig.25//26// # Renaming Fields27//28// To rename the key that mapstructure looks for, use the "mapstructure"29// tag and set a value directly. For example, to change the "username" example30// above to "user":31//32// type User struct {33// Username string `mapstructure:"user"`34// }35//36// # Embedded Structs and Squashing37//38// Embedded structs are treated as if they're another field with that name.39// By default, the two structs below are equivalent when decoding with40// mapstructure:41//42// type Person struct {43// Name string44// }45//46// type Friend struct {47// Person48// }49//50// type Friend struct {51// Person Person52// }53//54// This would require an input that looks like below:55//56// map[string]any{57// "person": map[string]any{"name": "alice"},58// }59//60// If your "person" value is NOT nested, then you can append ",squash" to61// your tag value and mapstructure will treat it as if the embedded struct62// were part of the struct directly. Example:63//64// type Friend struct {65// Person `mapstructure:",squash"`66// }67//68// Now the following input would be accepted:69//70// map[string]any{71// "name": "alice",72// }73//74// When decoding from a struct to a map, the squash tag squashes the struct75// fields into a single map. Using the example structs from above:76//77// Friend{Person: Person{Name: "alice"}}78//79// Will be decoded into a map:80//81// map[string]any{82// "name": "alice",83// }84//85// DecoderConfig has a field that changes the behavior of mapstructure86// to always squash embedded structs.87//88// # Remainder Values89//90// If there are any unmapped keys in the source value, mapstructure by91// default will silently ignore them. You can error by setting ErrorUnused92// in DecoderConfig. If you're using Metadata you can also maintain a slice93// of the unused keys.94//95// You can also use the ",remain" suffix on your tag to collect all unused96// values in a map. The field with this tag MUST be a map type and should97// probably be a "map[string]any" or "map[any]any".98// See example below:99//100// type Friend struct {101// Name string102// Other map[string]any `mapstructure:",remain"`103// }104//105// Given the input below, Other would be populated with the other106// values that weren't used (everything but "name"):107//108// map[string]any{109// "name": "bob",110// "address": "123 Maple St.",111// }112//113// # Omit Empty Values114//115// When decoding from a struct to any other value, you may use the116// ",omitempty" suffix on your tag to omit that value if it equates to117// the zero value, or a zero-length element. The zero value of all types is118// specified in the Go specification.119//120// For example, the zero type of a numeric type is zero ("0"). If the struct121// field value is zero and a numeric type, the field is empty, and it won't122// be encoded into the destination type. And likewise for the URLs field, if the123// slice is nil or empty, it won't be encoded into the destination type.124//125// type Source struct {126// Age int `mapstructure:",omitempty"`127// URLs []string `mapstructure:",omitempty"`128// }129//130// # Omit Zero Values131//132// When decoding from a struct to any other value, you may use the133// ",omitzero" suffix on your tag to omit that value if it equates to the zero134// value. The zero value of all types is specified in the Go specification.135//136// For example, the zero type of a numeric type is zero ("0"). If the struct137// field value is zero and a numeric type, the field is empty, and it won't138// be encoded into the destination type. And likewise for the URLs field, if the139// slice is nil, it won't be encoded into the destination type.140//141// Note that if the field is a slice, and it is empty but not nil, it will142// still be encoded into the destination type.143//144// type Source struct {145// Age int `mapstructure:",omitzero"`146// URLs []string `mapstructure:",omitzero"`147// }148//149// # Unexported fields150//151// Since unexported (private) struct fields cannot be set outside the package152// where they are defined, the decoder will simply skip them.153//154// For this output type definition:155//156// type Exported struct {157// private string // this unexported field will be skipped158// Public string159// }160//161// Using this map as input:162//163// map[string]any{164// "private": "I will be ignored",165// "Public": "I made it through!",166// }167//168// The following struct will be decoded:169//170// type Exported struct {171// private: "" // field is left with an empty string (zero value)172// Public: "I made it through!"173// }174//175// # Other Configuration176//177// mapstructure is highly configurable. See the DecoderConfig struct178// for other features and options that are supported.179package mapstructure180181import (182"encoding/json"183"fmt"184"reflect"185"sort"186"strconv"187"strings"188189"github.com/go-viper/mapstructure/v2/internal/errors"190)191192// DecodeHookFunc is the callback function that can be used for193// data transformations. See "DecodeHook" in the DecoderConfig194// struct.195//196// The type must be one of DecodeHookFuncType, DecodeHookFuncKind, or197// DecodeHookFuncValue.198// Values are a superset of Types (Values can return types), and Types are a199// superset of Kinds (Types can return Kinds) and are generally a richer thing200// to use, but Kinds are simpler if you only need those.201//202// The reason DecodeHookFunc is multi-typed is for backwards compatibility:203// we started with Kinds and then realized Types were the better solution,204// but have a promise to not break backwards compat so we now support205// both.206type DecodeHookFunc any207208// DecodeHookFuncType is a DecodeHookFunc which has complete information about209// the source and target types.210type DecodeHookFuncType func(reflect.Type, reflect.Type, any) (any, error)211212// DecodeHookFuncKind is a DecodeHookFunc which knows only the Kinds of the213// source and target types.214type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, any) (any, error)215216// DecodeHookFuncValue is a DecodeHookFunc which has complete access to both the source and target217// values.218type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (any, error)219220// DecoderConfig is the configuration that is used to create a new decoder221// and allows customization of various aspects of decoding.222type DecoderConfig struct {223// DecodeHook, if set, will be called before any decoding and any224// type conversion (if WeaklyTypedInput is on). This lets you modify225// the values before they're set down onto the resulting struct. The226// DecodeHook is called for every map and value in the input. This means227// that if a struct has embedded fields with squash tags the decode hook228// is called only once with all of the input data, not once for each229// embedded struct.230//231// If an error is returned, the entire decode will fail with that error.232DecodeHook DecodeHookFunc233234// If ErrorUnused is true, then it is an error for there to exist235// keys in the original map that were unused in the decoding process236// (extra keys).237ErrorUnused bool238239// If ErrorUnset is true, then it is an error for there to exist240// fields in the result that were not set in the decoding process241// (extra fields). This only applies to decoding to a struct. This242// will affect all nested structs as well.243ErrorUnset bool244245// AllowUnsetPointer, if set to true, will prevent fields with pointer types246// from being reported as unset, even if ErrorUnset is true and the field was247// not present in the input data. This allows pointer fields to be optional248// without triggering an error when they are missing.249AllowUnsetPointer bool250251// ZeroFields, if set to true, will zero fields before writing them.252// For example, a map will be emptied before decoded values are put in253// it. If this is false, a map will be merged.254ZeroFields bool255256// If WeaklyTypedInput is true, the decoder will make the following257// "weak" conversions:258//259// - bools to string (true = "1", false = "0")260// - numbers to string (base 10)261// - bools to int/uint (true = 1, false = 0)262// - strings to int/uint (base implied by prefix)263// - int to bool (true if value != 0)264// - string to bool (accepts: 1, t, T, TRUE, true, True, 0, f, F,265// FALSE, false, False. Anything else is an error)266// - empty array = empty map and vice versa267// - negative numbers to overflowed uint values (base 10)268// - slice of maps to a merged map269// - single values are converted to slices if required. Each270// element is weakly decoded. For example: "4" can become []int{4}271// if the target type is an int slice.272//273WeaklyTypedInput bool274275// Squash will squash embedded structs. A squash tag may also be276// added to an individual struct field using a tag. For example:277//278// type Parent struct {279// Child `mapstructure:",squash"`280// }281Squash bool282283// Metadata is the struct that will contain extra metadata about284// the decoding. If this is nil, then no metadata will be tracked.285Metadata *Metadata286287// Result is a pointer to the struct that will contain the decoded288// value.289Result any290291// The tag name that mapstructure reads for field names. This292// defaults to "mapstructure"293TagName string294295// The option of the value in the tag that indicates a field should296// be squashed. This defaults to "squash".297SquashTagOption string298299// IgnoreUntaggedFields ignores all struct fields without explicit300// TagName, comparable to `mapstructure:"-"` as default behaviour.301IgnoreUntaggedFields bool302303// MatchName is the function used to match the map key to the struct304// field name or tag. Defaults to `strings.EqualFold`. This can be used305// to implement case-sensitive tag values, support snake casing, etc.306MatchName func(mapKey, fieldName string) bool307308// DecodeNil, if set to true, will cause the DecodeHook (if present) to run309// even if the input is nil. This can be used to provide default values.310DecodeNil bool311}312313// A Decoder takes a raw interface value and turns it into structured314// data, keeping track of rich error information along the way in case315// anything goes wrong. Unlike the basic top-level Decode method, you can316// more finely control how the Decoder behaves using the DecoderConfig317// structure. The top-level Decode method is just a convenience that sets318// up the most basic Decoder.319type Decoder struct {320config *DecoderConfig321cachedDecodeHook func(from reflect.Value, to reflect.Value) (any, error)322}323324// Metadata contains information about decoding a structure that325// is tedious or difficult to get otherwise.326type Metadata struct {327// Keys are the keys of the structure which were successfully decoded328Keys []string329330// Unused is a slice of keys that were found in the raw value but331// weren't decoded since there was no matching field in the result interface332Unused []string333334// Unset is a slice of field names that were found in the result interface335// but weren't set in the decoding process since there was no matching value336// in the input337Unset []string338}339340// Decode takes an input structure and uses reflection to translate it to341// the output structure. output must be a pointer to a map or struct.342func Decode(input any, output any) error {343config := &DecoderConfig{344Metadata: nil,345Result: output,346}347348decoder, err := NewDecoder(config)349if err != nil {350return err351}352353return decoder.Decode(input)354}355356// WeakDecode is the same as Decode but is shorthand to enable357// WeaklyTypedInput. See DecoderConfig for more info.358func WeakDecode(input, output any) error {359config := &DecoderConfig{360Metadata: nil,361Result: output,362WeaklyTypedInput: true,363}364365decoder, err := NewDecoder(config)366if err != nil {367return err368}369370return decoder.Decode(input)371}372373// DecodeMetadata is the same as Decode, but is shorthand to374// enable metadata collection. See DecoderConfig for more info.375func DecodeMetadata(input any, output any, metadata *Metadata) error {376config := &DecoderConfig{377Metadata: metadata,378Result: output,379}380381decoder, err := NewDecoder(config)382if err != nil {383return err384}385386return decoder.Decode(input)387}388389// WeakDecodeMetadata is the same as Decode, but is shorthand to390// enable both WeaklyTypedInput and metadata collection. See391// DecoderConfig for more info.392func WeakDecodeMetadata(input any, output any, metadata *Metadata) error {393config := &DecoderConfig{394Metadata: metadata,395Result: output,396WeaklyTypedInput: true,397}398399decoder, err := NewDecoder(config)400if err != nil {401return err402}403404return decoder.Decode(input)405}406407// NewDecoder returns a new decoder for the given configuration. Once408// a decoder has been returned, the same configuration must not be used409// again.410func NewDecoder(config *DecoderConfig) (*Decoder, error) {411val := reflect.ValueOf(config.Result)412if val.Kind() != reflect.Ptr {413return nil, errors.New("result must be a pointer")414}415416val = val.Elem()417if !val.CanAddr() {418return nil, errors.New("result must be addressable (a pointer)")419}420421if config.Metadata != nil {422if config.Metadata.Keys == nil {423config.Metadata.Keys = make([]string, 0)424}425426if config.Metadata.Unused == nil {427config.Metadata.Unused = make([]string, 0)428}429430if config.Metadata.Unset == nil {431config.Metadata.Unset = make([]string, 0)432}433}434435if config.TagName == "" {436config.TagName = "mapstructure"437}438439if config.SquashTagOption == "" {440config.SquashTagOption = "squash"441}442443if config.MatchName == nil {444config.MatchName = strings.EqualFold445}446447result := &Decoder{448config: config,449}450if config.DecodeHook != nil {451result.cachedDecodeHook = cachedDecodeHook(config.DecodeHook)452}453454return result, nil455}456457// Decode decodes the given raw interface to the target pointer specified458// by the configuration.459func (d *Decoder) Decode(input any) error {460err := d.decode("", input, reflect.ValueOf(d.config.Result).Elem())461462// Retain some of the original behavior when multiple errors ocurr463var joinedErr interface{ Unwrap() []error }464if errors.As(err, &joinedErr) {465return fmt.Errorf("decoding failed due to the following error(s):\n\n%w", err)466}467468return err469}470471// isNil returns true if the input is nil or a typed nil pointer.472func isNil(input any) bool {473if input == nil {474return true475}476val := reflect.ValueOf(input)477return val.Kind() == reflect.Ptr && val.IsNil()478}479480// Decodes an unknown data type into a specific reflection value.481func (d *Decoder) decode(name string, input any, outVal reflect.Value) error {482var (483inputVal = reflect.ValueOf(input)484outputKind = getKind(outVal)485decodeNil = d.config.DecodeNil && d.cachedDecodeHook != nil486)487if isNil(input) {488// Typed nils won't match the "input == nil" below, so reset input.489input = nil490}491if input == nil {492// If the data is nil, then we don't set anything, unless ZeroFields is set493// to true.494if d.config.ZeroFields {495outVal.Set(reflect.Zero(outVal.Type()))496497if d.config.Metadata != nil && name != "" {498d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)499}500}501if !decodeNil {502return nil503}504}505if !inputVal.IsValid() {506if !decodeNil {507// If the input value is invalid, then we just set the value508// to be the zero value.509outVal.Set(reflect.Zero(outVal.Type()))510if d.config.Metadata != nil && name != "" {511d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)512}513return nil514}515// Hooks need a valid inputVal, so reset it to zero value of outVal type.516switch outputKind {517case reflect.Struct, reflect.Map:518var mapVal map[string]any519inputVal = reflect.ValueOf(mapVal) // create nil map pointer520case reflect.Slice, reflect.Array:521var sliceVal []any522inputVal = reflect.ValueOf(sliceVal) // create nil slice pointer523default:524inputVal = reflect.Zero(outVal.Type())525}526}527528if d.cachedDecodeHook != nil {529// We have a DecodeHook, so let's pre-process the input.530var err error531input, err = d.cachedDecodeHook(inputVal, outVal)532if err != nil {533return newDecodeError(name, err)534}535}536if isNil(input) {537return nil538}539540var err error541addMetaKey := true542switch outputKind {543case reflect.Bool:544err = d.decodeBool(name, input, outVal)545case reflect.Interface:546err = d.decodeBasic(name, input, outVal)547case reflect.String:548err = d.decodeString(name, input, outVal)549case reflect.Int:550err = d.decodeInt(name, input, outVal)551case reflect.Uint:552err = d.decodeUint(name, input, outVal)553case reflect.Float32:554err = d.decodeFloat(name, input, outVal)555case reflect.Complex64:556err = d.decodeComplex(name, input, outVal)557case reflect.Struct:558err = d.decodeStruct(name, input, outVal)559case reflect.Map:560err = d.decodeMap(name, input, outVal)561case reflect.Ptr:562addMetaKey, err = d.decodePtr(name, input, outVal)563case reflect.Slice:564err = d.decodeSlice(name, input, outVal)565case reflect.Array:566err = d.decodeArray(name, input, outVal)567case reflect.Func:568err = d.decodeFunc(name, input, outVal)569default:570// If we reached this point then we weren't able to decode it571return newDecodeError(name, fmt.Errorf("unsupported type: %s", outputKind))572}573574// If we reached here, then we successfully decoded SOMETHING, so575// mark the key as used if we're tracking metainput.576if addMetaKey && d.config.Metadata != nil && name != "" {577d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)578}579580return err581}582583// This decodes a basic type (bool, int, string, etc.) and sets the584// value to "data" of that type.585func (d *Decoder) decodeBasic(name string, data any, val reflect.Value) error {586if val.IsValid() && val.Elem().IsValid() {587elem := val.Elem()588589// If we can't address this element, then its not writable. Instead,590// we make a copy of the value (which is a pointer and therefore591// writable), decode into that, and replace the whole value.592copied := false593if !elem.CanAddr() {594copied = true595596// Make *T597copy := reflect.New(elem.Type())598599// *T = elem600copy.Elem().Set(elem)601602// Set elem so we decode into it603elem = copy604}605606// Decode. If we have an error then return. We also return right607// away if we're not a copy because that means we decoded directly.608if err := d.decode(name, data, elem); err != nil || !copied {609return err610}611612// If we're a copy, we need to set te final result613val.Set(elem.Elem())614return nil615}616617dataVal := reflect.ValueOf(data)618619// If the input data is a pointer, and the assigned type is the dereference620// of that exact pointer, then indirect it so that we can assign it.621// Example: *string to string622if dataVal.Kind() == reflect.Ptr && dataVal.Type().Elem() == val.Type() {623dataVal = reflect.Indirect(dataVal)624}625626if !dataVal.IsValid() {627dataVal = reflect.Zero(val.Type())628}629630dataValType := dataVal.Type()631if !dataValType.AssignableTo(val.Type()) {632return newDecodeError(name, &UnconvertibleTypeError{633Expected: val,634Value: data,635})636}637638val.Set(dataVal)639return nil640}641642func (d *Decoder) decodeString(name string, data any, val reflect.Value) error {643dataVal := reflect.Indirect(reflect.ValueOf(data))644dataKind := getKind(dataVal)645646converted := true647switch {648case dataKind == reflect.String:649val.SetString(dataVal.String())650case dataKind == reflect.Bool && d.config.WeaklyTypedInput:651if dataVal.Bool() {652val.SetString("1")653} else {654val.SetString("0")655}656case dataKind == reflect.Int && d.config.WeaklyTypedInput:657val.SetString(strconv.FormatInt(dataVal.Int(), 10))658case dataKind == reflect.Uint && d.config.WeaklyTypedInput:659val.SetString(strconv.FormatUint(dataVal.Uint(), 10))660case dataKind == reflect.Float32 && d.config.WeaklyTypedInput:661val.SetString(strconv.FormatFloat(dataVal.Float(), 'f', -1, 64))662case dataKind == reflect.Slice && d.config.WeaklyTypedInput,663dataKind == reflect.Array && d.config.WeaklyTypedInput:664dataType := dataVal.Type()665elemKind := dataType.Elem().Kind()666switch elemKind {667case reflect.Uint8:668var uints []uint8669if dataKind == reflect.Array {670uints = make([]uint8, dataVal.Len(), dataVal.Len())671for i := range uints {672uints[i] = dataVal.Index(i).Interface().(uint8)673}674} else {675uints = dataVal.Interface().([]uint8)676}677val.SetString(string(uints))678default:679converted = false680}681default:682converted = false683}684685if !converted {686return newDecodeError(name, &UnconvertibleTypeError{687Expected: val,688Value: data,689})690}691692return nil693}694695func (d *Decoder) decodeInt(name string, data any, val reflect.Value) error {696dataVal := reflect.Indirect(reflect.ValueOf(data))697dataKind := getKind(dataVal)698dataType := dataVal.Type()699700switch {701case dataKind == reflect.Int:702val.SetInt(dataVal.Int())703case dataKind == reflect.Uint:704val.SetInt(int64(dataVal.Uint()))705case dataKind == reflect.Float32:706val.SetInt(int64(dataVal.Float()))707case dataKind == reflect.Bool && d.config.WeaklyTypedInput:708if dataVal.Bool() {709val.SetInt(1)710} else {711val.SetInt(0)712}713case dataKind == reflect.String && d.config.WeaklyTypedInput:714str := dataVal.String()715if str == "" {716str = "0"717}718719i, err := strconv.ParseInt(str, 0, val.Type().Bits())720if err == nil {721val.SetInt(i)722} else {723return newDecodeError(name, &ParseError{724Expected: val,725Value: data,726Err: wrapStrconvNumError(err),727})728}729case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":730jn := data.(json.Number)731i, err := jn.Int64()732if err != nil {733return newDecodeError(name, &ParseError{734Expected: val,735Value: data,736Err: err,737})738}739val.SetInt(i)740default:741return newDecodeError(name, &UnconvertibleTypeError{742Expected: val,743Value: data,744})745}746747return nil748}749750func (d *Decoder) decodeUint(name string, data any, val reflect.Value) error {751dataVal := reflect.Indirect(reflect.ValueOf(data))752dataKind := getKind(dataVal)753dataType := dataVal.Type()754755switch {756case dataKind == reflect.Int:757i := dataVal.Int()758if i < 0 && !d.config.WeaklyTypedInput {759return newDecodeError(name, &ParseError{760Expected: val,761Value: data,762Err: fmt.Errorf("%d overflows uint", i),763})764}765val.SetUint(uint64(i))766case dataKind == reflect.Uint:767val.SetUint(dataVal.Uint())768case dataKind == reflect.Float32:769f := dataVal.Float()770if f < 0 && !d.config.WeaklyTypedInput {771return newDecodeError(name, &ParseError{772Expected: val,773Value: data,774Err: fmt.Errorf("%f overflows uint", f),775})776}777val.SetUint(uint64(f))778case dataKind == reflect.Bool && d.config.WeaklyTypedInput:779if dataVal.Bool() {780val.SetUint(1)781} else {782val.SetUint(0)783}784case dataKind == reflect.String && d.config.WeaklyTypedInput:785str := dataVal.String()786if str == "" {787str = "0"788}789790i, err := strconv.ParseUint(str, 0, val.Type().Bits())791if err == nil {792val.SetUint(i)793} else {794return newDecodeError(name, &ParseError{795Expected: val,796Value: data,797Err: wrapStrconvNumError(err),798})799}800case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":801jn := data.(json.Number)802i, err := strconv.ParseUint(string(jn), 0, 64)803if err != nil {804return newDecodeError(name, &ParseError{805Expected: val,806Value: data,807Err: wrapStrconvNumError(err),808})809}810val.SetUint(i)811default:812return newDecodeError(name, &UnconvertibleTypeError{813Expected: val,814Value: data,815})816}817818return nil819}820821func (d *Decoder) decodeBool(name string, data any, val reflect.Value) error {822dataVal := reflect.Indirect(reflect.ValueOf(data))823dataKind := getKind(dataVal)824825switch {826case dataKind == reflect.Bool:827val.SetBool(dataVal.Bool())828case dataKind == reflect.Int && d.config.WeaklyTypedInput:829val.SetBool(dataVal.Int() != 0)830case dataKind == reflect.Uint && d.config.WeaklyTypedInput:831val.SetBool(dataVal.Uint() != 0)832case dataKind == reflect.Float32 && d.config.WeaklyTypedInput:833val.SetBool(dataVal.Float() != 0)834case dataKind == reflect.String && d.config.WeaklyTypedInput:835b, err := strconv.ParseBool(dataVal.String())836if err == nil {837val.SetBool(b)838} else if dataVal.String() == "" {839val.SetBool(false)840} else {841return newDecodeError(name, &ParseError{842Expected: val,843Value: data,844Err: wrapStrconvNumError(err),845})846}847default:848return newDecodeError(name, &UnconvertibleTypeError{849Expected: val,850Value: data,851})852}853854return nil855}856857func (d *Decoder) decodeFloat(name string, data any, val reflect.Value) error {858dataVal := reflect.Indirect(reflect.ValueOf(data))859dataKind := getKind(dataVal)860dataType := dataVal.Type()861862switch {863case dataKind == reflect.Int:864val.SetFloat(float64(dataVal.Int()))865case dataKind == reflect.Uint:866val.SetFloat(float64(dataVal.Uint()))867case dataKind == reflect.Float32:868val.SetFloat(dataVal.Float())869case dataKind == reflect.Bool && d.config.WeaklyTypedInput:870if dataVal.Bool() {871val.SetFloat(1)872} else {873val.SetFloat(0)874}875case dataKind == reflect.String && d.config.WeaklyTypedInput:876str := dataVal.String()877if str == "" {878str = "0"879}880881f, err := strconv.ParseFloat(str, val.Type().Bits())882if err == nil {883val.SetFloat(f)884} else {885return newDecodeError(name, &ParseError{886Expected: val,887Value: data,888Err: wrapStrconvNumError(err),889})890}891case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":892jn := data.(json.Number)893i, err := jn.Float64()894if err != nil {895return newDecodeError(name, &ParseError{896Expected: val,897Value: data,898Err: err,899})900}901val.SetFloat(i)902default:903return newDecodeError(name, &UnconvertibleTypeError{904Expected: val,905Value: data,906})907}908909return nil910}911912func (d *Decoder) decodeComplex(name string, data any, val reflect.Value) error {913dataVal := reflect.Indirect(reflect.ValueOf(data))914dataKind := getKind(dataVal)915916switch {917case dataKind == reflect.Complex64:918val.SetComplex(dataVal.Complex())919default:920return newDecodeError(name, &UnconvertibleTypeError{921Expected: val,922Value: data,923})924}925926return nil927}928929func (d *Decoder) decodeMap(name string, data any, val reflect.Value) error {930valType := val.Type()931valKeyType := valType.Key()932valElemType := valType.Elem()933934// By default we overwrite keys in the current map935valMap := val936937// If the map is nil or we're purposely zeroing fields, make a new map938if valMap.IsNil() || d.config.ZeroFields {939// Make a new map to hold our result940mapType := reflect.MapOf(valKeyType, valElemType)941valMap = reflect.MakeMap(mapType)942}943944dataVal := reflect.ValueOf(data)945946// Resolve any levels of indirection947for dataVal.Kind() == reflect.Pointer {948dataVal = reflect.Indirect(dataVal)949}950951// Check input type and based on the input type jump to the proper func952switch dataVal.Kind() {953case reflect.Map:954return d.decodeMapFromMap(name, dataVal, val, valMap)955956case reflect.Struct:957return d.decodeMapFromStruct(name, dataVal, val, valMap)958959case reflect.Array, reflect.Slice:960if d.config.WeaklyTypedInput {961return d.decodeMapFromSlice(name, dataVal, val, valMap)962}963964fallthrough965966default:967return newDecodeError(name, &UnconvertibleTypeError{968Expected: val,969Value: data,970})971}972}973974func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {975// Special case for BC reasons (covered by tests)976if dataVal.Len() == 0 {977val.Set(valMap)978return nil979}980981for i := 0; i < dataVal.Len(); i++ {982err := d.decode(983name+"["+strconv.Itoa(i)+"]",984dataVal.Index(i).Interface(), val)985if err != nil {986return err987}988}989990return nil991}992993func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {994valType := val.Type()995valKeyType := valType.Key()996valElemType := valType.Elem()997998// Accumulate errors999var errs []error10001001// If the input data is empty, then we just match what the input data is.1002if dataVal.Len() == 0 {1003if dataVal.IsNil() {1004if !val.IsNil() {1005val.Set(dataVal)1006}1007} else {1008// Set to empty allocated value1009val.Set(valMap)1010}10111012return nil1013}10141015for _, k := range dataVal.MapKeys() {1016fieldName := name + "[" + k.String() + "]"10171018// First decode the key into the proper type1019currentKey := reflect.Indirect(reflect.New(valKeyType))1020if err := d.decode(fieldName, k.Interface(), currentKey); err != nil {1021errs = append(errs, err)1022continue1023}10241025// Next decode the data into the proper type1026v := dataVal.MapIndex(k).Interface()1027currentVal := reflect.Indirect(reflect.New(valElemType))1028if err := d.decode(fieldName, v, currentVal); err != nil {1029errs = append(errs, err)1030continue1031}10321033valMap.SetMapIndex(currentKey, currentVal)1034}10351036// Set the built up map to the value1037val.Set(valMap)10381039return errors.Join(errs...)1040}10411042func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {1043typ := dataVal.Type()1044for i := 0; i < typ.NumField(); i++ {1045// Get the StructField first since this is a cheap operation. If the1046// field is unexported, then ignore it.1047f := typ.Field(i)1048if f.PkgPath != "" {1049continue1050}10511052// Next get the actual value of this field and verify it is assignable1053// to the map value.1054v := dataVal.Field(i)1055if !v.Type().AssignableTo(valMap.Type().Elem()) {1056return newDecodeError(1057name+"."+f.Name,1058fmt.Errorf("cannot assign type %q to map value field of type %q", v.Type(), valMap.Type().Elem()),1059)1060}10611062tagValue := f.Tag.Get(d.config.TagName)1063keyName := f.Name10641065if tagValue == "" && d.config.IgnoreUntaggedFields {1066continue1067}10681069// If Squash is set in the config, we squash the field down.1070squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous10711072v = dereferencePtrToStructIfNeeded(v, d.config.TagName)10731074// Determine the name of the key in the map1075if index := strings.Index(tagValue, ","); index != -1 {1076if tagValue[:index] == "-" {1077continue1078}1079// If "omitempty" is specified in the tag, it ignores empty values.1080if strings.Index(tagValue[index+1:], "omitempty") != -1 && isEmptyValue(v) {1081continue1082}10831084// If "omitzero" is specified in the tag, it ignores zero values.1085if strings.Index(tagValue[index+1:], "omitzero") != -1 && v.IsZero() {1086continue1087}10881089// If "squash" is specified in the tag, we squash the field down.1090squash = squash || strings.Contains(tagValue[index+1:], d.config.SquashTagOption)1091if squash {1092// When squashing, the embedded type can be a pointer to a struct.1093if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct {1094v = v.Elem()1095}10961097// The final type must be a struct1098if v.Kind() != reflect.Struct {1099return newDecodeError(1100name+"."+f.Name,1101fmt.Errorf("cannot squash non-struct type %q", v.Type()),1102)1103}1104} else {1105if strings.Index(tagValue[index+1:], "remain") != -1 {1106if v.Kind() != reflect.Map {1107return newDecodeError(1108name+"."+f.Name,1109fmt.Errorf("error remain-tag field with invalid type: %q", v.Type()),1110)1111}11121113ptr := v.MapRange()1114for ptr.Next() {1115valMap.SetMapIndex(ptr.Key(), ptr.Value())1116}1117continue1118}1119}1120if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" {1121keyName = keyNameTagValue1122}1123} else if len(tagValue) > 0 {1124if tagValue == "-" {1125continue1126}1127keyName = tagValue1128}11291130switch v.Kind() {1131// this is an embedded struct, so handle it differently1132case reflect.Struct:1133x := reflect.New(v.Type())1134x.Elem().Set(v)11351136vType := valMap.Type()1137vKeyType := vType.Key()1138vElemType := vType.Elem()1139mType := reflect.MapOf(vKeyType, vElemType)1140vMap := reflect.MakeMap(mType)11411142// Creating a pointer to a map so that other methods can completely1143// overwrite the map if need be (looking at you decodeMapFromMap). The1144// indirection allows the underlying map to be settable (CanSet() == true)1145// where as reflect.MakeMap returns an unsettable map.1146addrVal := reflect.New(vMap.Type())1147reflect.Indirect(addrVal).Set(vMap)11481149err := d.decode(keyName, x.Interface(), reflect.Indirect(addrVal))1150if err != nil {1151return err1152}11531154// the underlying map may have been completely overwritten so pull1155// it indirectly out of the enclosing value.1156vMap = reflect.Indirect(addrVal)11571158if squash {1159for _, k := range vMap.MapKeys() {1160valMap.SetMapIndex(k, vMap.MapIndex(k))1161}1162} else {1163valMap.SetMapIndex(reflect.ValueOf(keyName), vMap)1164}11651166default:1167valMap.SetMapIndex(reflect.ValueOf(keyName), v)1168}1169}11701171if val.CanAddr() {1172val.Set(valMap)1173}11741175return nil1176}11771178func (d *Decoder) decodePtr(name string, data any, val reflect.Value) (bool, error) {1179// If the input data is nil, then we want to just set the output1180// pointer to be nil as well.1181isNil := data == nil1182if !isNil {1183switch v := reflect.Indirect(reflect.ValueOf(data)); v.Kind() {1184case reflect.Chan,1185reflect.Func,1186reflect.Interface,1187reflect.Map,1188reflect.Ptr,1189reflect.Slice:1190isNil = v.IsNil()1191}1192}1193if isNil {1194if !val.IsNil() && val.CanSet() {1195nilValue := reflect.New(val.Type()).Elem()1196val.Set(nilValue)1197}11981199return true, nil1200}12011202// Create an element of the concrete (non pointer) type and decode1203// into that. Then set the value of the pointer to this type.1204valType := val.Type()1205valElemType := valType.Elem()1206if val.CanSet() {1207realVal := val1208if realVal.IsNil() || d.config.ZeroFields {1209realVal = reflect.New(valElemType)1210}12111212if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil {1213return false, err1214}12151216val.Set(realVal)1217} else {1218if err := d.decode(name, data, reflect.Indirect(val)); err != nil {1219return false, err1220}1221}1222return false, nil1223}12241225func (d *Decoder) decodeFunc(name string, data any, val reflect.Value) error {1226// Create an element of the concrete (non pointer) type and decode1227// into that. Then set the value of the pointer to this type.1228dataVal := reflect.Indirect(reflect.ValueOf(data))1229if val.Type() != dataVal.Type() {1230return newDecodeError(name, &UnconvertibleTypeError{1231Expected: val,1232Value: data,1233})1234}1235val.Set(dataVal)1236return nil1237}12381239func (d *Decoder) decodeSlice(name string, data any, val reflect.Value) error {1240dataVal := reflect.Indirect(reflect.ValueOf(data))1241dataValKind := dataVal.Kind()1242valType := val.Type()1243valElemType := valType.Elem()1244sliceType := reflect.SliceOf(valElemType)12451246// If we have a non array/slice type then we first attempt to convert.1247if dataValKind != reflect.Array && dataValKind != reflect.Slice {1248if d.config.WeaklyTypedInput {1249switch {1250// Slice and array we use the normal logic1251case dataValKind == reflect.Slice, dataValKind == reflect.Array:1252break12531254// Empty maps turn into empty slices1255case dataValKind == reflect.Map:1256if dataVal.Len() == 0 {1257val.Set(reflect.MakeSlice(sliceType, 0, 0))1258return nil1259}1260// Create slice of maps of other sizes1261return d.decodeSlice(name, []any{data}, val)12621263case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8:1264return d.decodeSlice(name, []byte(dataVal.String()), val)12651266// All other types we try to convert to the slice type1267// and "lift" it into it. i.e. a string becomes a string slice.1268default:1269// Just re-try this function with data as a slice.1270return d.decodeSlice(name, []any{data}, val)1271}1272}12731274return newDecodeError(name,1275fmt.Errorf("source data must be an array or slice, got %s", dataValKind))1276}12771278// If the input value is nil, then don't allocate since empty != nil1279if dataValKind != reflect.Array && dataVal.IsNil() {1280return nil1281}12821283valSlice := val1284if valSlice.IsNil() || d.config.ZeroFields {1285// Make a new slice to hold our result, same size as the original data.1286valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len())1287} else if valSlice.Len() > dataVal.Len() {1288valSlice = valSlice.Slice(0, dataVal.Len())1289}12901291// Accumulate any errors1292var errs []error12931294for i := 0; i < dataVal.Len(); i++ {1295currentData := dataVal.Index(i).Interface()1296for valSlice.Len() <= i {1297valSlice = reflect.Append(valSlice, reflect.Zero(valElemType))1298}1299currentField := valSlice.Index(i)13001301fieldName := name + "[" + strconv.Itoa(i) + "]"1302if err := d.decode(fieldName, currentData, currentField); err != nil {1303errs = append(errs, err)1304}1305}13061307// Finally, set the value to the slice we built up1308val.Set(valSlice)13091310return errors.Join(errs...)1311}13121313func (d *Decoder) decodeArray(name string, data any, val reflect.Value) error {1314dataVal := reflect.Indirect(reflect.ValueOf(data))1315dataValKind := dataVal.Kind()1316valType := val.Type()1317valElemType := valType.Elem()1318arrayType := reflect.ArrayOf(valType.Len(), valElemType)13191320valArray := val13211322if isComparable(valArray) && valArray.Interface() == reflect.Zero(valArray.Type()).Interface() || d.config.ZeroFields {1323// Check input type1324if dataValKind != reflect.Array && dataValKind != reflect.Slice {1325if d.config.WeaklyTypedInput {1326switch {1327// Empty maps turn into empty arrays1328case dataValKind == reflect.Map:1329if dataVal.Len() == 0 {1330val.Set(reflect.Zero(arrayType))1331return nil1332}13331334// All other types we try to convert to the array type1335// and "lift" it into it. i.e. a string becomes a string array.1336default:1337// Just re-try this function with data as a slice.1338return d.decodeArray(name, []any{data}, val)1339}1340}13411342return newDecodeError(name,1343fmt.Errorf("source data must be an array or slice, got %s", dataValKind))13441345}1346if dataVal.Len() > arrayType.Len() {1347return newDecodeError(name,1348fmt.Errorf("expected source data to have length less or equal to %d, got %d", arrayType.Len(), dataVal.Len()))1349}13501351// Make a new array to hold our result, same size as the original data.1352valArray = reflect.New(arrayType).Elem()1353}13541355// Accumulate any errors1356var errs []error13571358for i := 0; i < dataVal.Len(); i++ {1359currentData := dataVal.Index(i).Interface()1360currentField := valArray.Index(i)13611362fieldName := name + "[" + strconv.Itoa(i) + "]"1363if err := d.decode(fieldName, currentData, currentField); err != nil {1364errs = append(errs, err)1365}1366}13671368// Finally, set the value to the array we built up1369val.Set(valArray)13701371return errors.Join(errs...)1372}13731374func (d *Decoder) decodeStruct(name string, data any, val reflect.Value) error {1375dataVal := reflect.Indirect(reflect.ValueOf(data))13761377// If the type of the value to write to and the data match directly,1378// then we just set it directly instead of recursing into the structure.1379if dataVal.Type() == val.Type() {1380val.Set(dataVal)1381return nil1382}13831384dataValKind := dataVal.Kind()1385switch dataValKind {1386case reflect.Map:1387return d.decodeStructFromMap(name, dataVal, val)13881389case reflect.Struct:1390// Not the most efficient way to do this but we can optimize later if1391// we want to. To convert from struct to struct we go to map first1392// as an intermediary.13931394// Make a new map to hold our result1395mapType := reflect.TypeOf((map[string]any)(nil))1396mval := reflect.MakeMap(mapType)13971398// Creating a pointer to a map so that other methods can completely1399// overwrite the map if need be (looking at you decodeMapFromMap). The1400// indirection allows the underlying map to be settable (CanSet() == true)1401// where as reflect.MakeMap returns an unsettable map.1402addrVal := reflect.New(mval.Type())14031404reflect.Indirect(addrVal).Set(mval)1405if err := d.decodeMapFromStruct(name, dataVal, reflect.Indirect(addrVal), mval); err != nil {1406return err1407}14081409result := d.decodeStructFromMap(name, reflect.Indirect(addrVal), val)1410return result14111412default:1413return newDecodeError(name,1414fmt.Errorf("expected a map or struct, got %q", dataValKind))1415}1416}14171418func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) error {1419dataValType := dataVal.Type()1420if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface {1421return newDecodeError(name,1422fmt.Errorf("needs a map with string keys, has %q keys", kind))1423}14241425dataValKeys := make(map[reflect.Value]struct{})1426dataValKeysUnused := make(map[any]struct{})1427for _, dataValKey := range dataVal.MapKeys() {1428dataValKeys[dataValKey] = struct{}{}1429dataValKeysUnused[dataValKey.Interface()] = struct{}{}1430}14311432targetValKeysUnused := make(map[any]struct{})14331434var errs []error14351436// This slice will keep track of all the structs we'll be decoding.1437// There can be more than one struct if there are embedded structs1438// that are squashed.1439structs := make([]reflect.Value, 1, 5)1440structs[0] = val14411442// Compile the list of all the fields that we're going to be decoding1443// from all the structs.1444type field struct {1445field reflect.StructField1446val reflect.Value1447}14481449// remainField is set to a valid field set with the "remain" tag if1450// we are keeping track of remaining values.1451var remainField *field14521453fields := []field{}1454for len(structs) > 0 {1455structVal := structs[0]1456structs = structs[1:]14571458structType := structVal.Type()14591460for i := 0; i < structType.NumField(); i++ {1461fieldType := structType.Field(i)1462fieldVal := structVal.Field(i)1463if fieldVal.Kind() == reflect.Ptr && fieldVal.Elem().Kind() == reflect.Struct {1464// Handle embedded struct pointers as embedded structs.1465fieldVal = fieldVal.Elem()1466}14671468// If "squash" is specified in the tag, we squash the field down.1469squash := d.config.Squash && fieldVal.Kind() == reflect.Struct && fieldType.Anonymous1470remain := false14711472// We always parse the tags cause we're looking for other tags too1473tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",")1474for _, tag := range tagParts[1:] {1475if tag == d.config.SquashTagOption {1476squash = true1477break1478}14791480if tag == "remain" {1481remain = true1482break1483}1484}14851486if squash {1487switch fieldVal.Kind() {1488case reflect.Struct:1489structs = append(structs, fieldVal)1490case reflect.Interface:1491if !fieldVal.IsNil() {1492structs = append(structs, fieldVal.Elem().Elem())1493}1494default:1495errs = append(errs, newDecodeError(1496name+"."+fieldType.Name,1497fmt.Errorf("unsupported type for squash: %s", fieldVal.Kind()),1498))1499}1500continue1501}15021503// Build our field1504if remain {1505remainField = &field{fieldType, fieldVal}1506} else {1507// Normal struct field, store it away1508fields = append(fields, field{fieldType, fieldVal})1509}1510}1511}15121513// for fieldType, field := range fields {1514for _, f := range fields {1515field, fieldValue := f.field, f.val1516fieldName := field.Name15171518tagValue := field.Tag.Get(d.config.TagName)1519if tagValue == "" && d.config.IgnoreUntaggedFields {1520continue1521}1522tagValue = strings.SplitN(tagValue, ",", 2)[0]1523if tagValue != "" {1524fieldName = tagValue1525}15261527rawMapKey := reflect.ValueOf(fieldName)1528rawMapVal := dataVal.MapIndex(rawMapKey)1529if !rawMapVal.IsValid() {1530// Do a slower search by iterating over each key and1531// doing case-insensitive search.1532for dataValKey := range dataValKeys {1533mK, ok := dataValKey.Interface().(string)1534if !ok {1535// Not a string key1536continue1537}15381539if d.config.MatchName(mK, fieldName) {1540rawMapKey = dataValKey1541rawMapVal = dataVal.MapIndex(dataValKey)1542break1543}1544}15451546if !rawMapVal.IsValid() {1547// There was no matching key in the map for the value in1548// the struct. Remember it for potential errors and metadata.1549if !(d.config.AllowUnsetPointer && fieldValue.Kind() == reflect.Ptr) {1550targetValKeysUnused[fieldName] = struct{}{}1551}1552continue1553}1554}15551556if !fieldValue.IsValid() {1557// This should never happen1558panic("field is not valid")1559}15601561// If we can't set the field, then it is unexported or something,1562// and we just continue onwards.1563if !fieldValue.CanSet() {1564continue1565}15661567// Delete the key we're using from the unused map so we stop tracking1568delete(dataValKeysUnused, rawMapKey.Interface())15691570// If the name is empty string, then we're at the root, and we1571// don't dot-join the fields.1572if name != "" {1573fieldName = name + "." + fieldName1574}15751576if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil {1577errs = append(errs, err)1578}1579}15801581// If we have a "remain"-tagged field and we have unused keys then1582// we put the unused keys directly into the remain field.1583if remainField != nil && len(dataValKeysUnused) > 0 {1584// Build a map of only the unused values1585remain := map[any]any{}1586for key := range dataValKeysUnused {1587remain[key] = dataVal.MapIndex(reflect.ValueOf(key)).Interface()1588}15891590// Decode it as-if we were just decoding this map onto our map.1591if err := d.decodeMap(name, remain, remainField.val); err != nil {1592errs = append(errs, err)1593}15941595// Set the map to nil so we have none so that the next check will1596// not error (ErrorUnused)1597dataValKeysUnused = nil1598}15991600if d.config.ErrorUnused && len(dataValKeysUnused) > 0 {1601keys := make([]string, 0, len(dataValKeysUnused))1602for rawKey := range dataValKeysUnused {1603keys = append(keys, rawKey.(string))1604}1605sort.Strings(keys)16061607errs = append(errs, newDecodeError(1608name,1609fmt.Errorf("has invalid keys: %s", strings.Join(keys, ", ")),1610))1611}16121613if d.config.ErrorUnset && len(targetValKeysUnused) > 0 {1614keys := make([]string, 0, len(targetValKeysUnused))1615for rawKey := range targetValKeysUnused {1616keys = append(keys, rawKey.(string))1617}1618sort.Strings(keys)16191620errs = append(errs, newDecodeError(1621name,1622fmt.Errorf("has unset fields: %s", strings.Join(keys, ", ")),1623))1624}16251626if err := errors.Join(errs...); err != nil {1627return err1628}16291630// Add the unused keys to the list of unused keys if we're tracking metadata1631if d.config.Metadata != nil {1632for rawKey := range dataValKeysUnused {1633key := rawKey.(string)1634if name != "" {1635key = name + "." + key1636}16371638d.config.Metadata.Unused = append(d.config.Metadata.Unused, key)1639}1640for rawKey := range targetValKeysUnused {1641key := rawKey.(string)1642if name != "" {1643key = name + "." + key1644}16451646d.config.Metadata.Unset = append(d.config.Metadata.Unset, key)1647}1648}16491650return nil1651}16521653func isEmptyValue(v reflect.Value) bool {1654switch getKind(v) {1655case reflect.Array, reflect.Map, reflect.Slice, reflect.String:1656return v.Len() == 01657case reflect.Bool:1658return !v.Bool()1659case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:1660return v.Int() == 01661case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:1662return v.Uint() == 01663case reflect.Float32, reflect.Float64:1664return v.Float() == 01665case reflect.Interface, reflect.Ptr:1666return v.IsNil()1667}1668return false1669}16701671func getKind(val reflect.Value) reflect.Kind {1672kind := val.Kind()16731674switch {1675case kind >= reflect.Int && kind <= reflect.Int64:1676return reflect.Int1677case kind >= reflect.Uint && kind <= reflect.Uint64:1678return reflect.Uint1679case kind >= reflect.Float32 && kind <= reflect.Float64:1680return reflect.Float321681case kind >= reflect.Complex64 && kind <= reflect.Complex128:1682return reflect.Complex641683default:1684return kind1685}1686}16871688func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool, tagName string) bool {1689for i := 0; i < typ.NumField(); i++ {1690f := typ.Field(i)1691if f.PkgPath == "" && !checkMapstructureTags { // check for unexported fields1692return true1693}1694if checkMapstructureTags && f.Tag.Get(tagName) != "" { // check for mapstructure tags inside1695return true1696}1697}1698return false1699}17001701func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Value {1702if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {1703return v1704}1705deref := v.Elem()1706derefT := deref.Type()1707if isStructTypeConvertibleToMap(derefT, true, tagName) {1708return deref1709}1710return v1711}171217131714