mapstructure icon indicating copy to clipboard operation
mapstructure copied to clipboard

How to use mapstructure with google uuid

Open eloo opened this issue 5 years ago • 8 comments

Hi,

i'm just trying to decode a map into a struct with uuid but it looks like its not working out of the box.

So is there a trick to get mapstructure working with uuid of google?

Here is a small playground. https://play.golang.org/p/Gn08PEr9shS

Thanks

eloo avatar Jan 27 '21 14:01 eloo

You could create your own hook, something like:

func StringToUUIDHookFunc() DecodeHookFunc {
	return func(
		f reflect.Type,
		t reflect.Type,
		data interface{}) (interface{}, error) {
		if f.Kind() != reflect.String {
			return data, nil
		}
		if t != reflect.TypeOf(uuid.UUID{}) {
			return data, nil
		}

		return uuid.Parse(data.(string))
	}
}

alex-rufo avatar Jan 28 '21 17:01 alex-rufo

ah okay.. how should this be used exactly in the decode function?

eloo avatar Jan 28 '21 22:01 eloo

You need to set the hook in the configuration, for example:

func decode(input, output interface{}) error {
	config := &mapstructure.DecoderConfig{
		DecodeHook: mapstructure.ComposeDecodeHookFunc(
			stringToUUIDHookFunc(),
		),
		Result: &output,
	}

	decoder, err := mapstructure.NewDecoder(config)
	if err != nil {
		return err
	}

	return decoder.Decode(input)
}

func stringToUUIDHookFunc() mapstructure.DecodeHookFunc {
	return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
		if f.Kind() != reflect.String {
			return data, nil
		}
		if t != reflect.TypeOf(uuid.UUID{}) {
			return data, nil
		}

		return uuid.Parse(data.(string))
	}
}

alex-rufo avatar Jan 29 '21 08:01 alex-rufo

@alex-rufo Thanks for the example.

maybe this could be added somewhere as example? i guess UUID is a common use case for a lot of devs?

eloo avatar Jan 29 '21 08:01 eloo

Hi,

@alex-rufo

Is it possible to use a custom hook when converting (in the other direction) from a struct to a map ?

I am using the shopspring/decimal package and the monetary type is decimal.Decimal - I would like to represent it as either a float or string in my map,

How can I achieve this?

Thank you

https://play.golang.org/p/IVR-c4rGr0Q

asarandi avatar Feb 05 '21 07:02 asarandi

Hi,

@alex-rufo

Is it possible to use a custom hook when converting (in the other direction) from a struct to a map ?

I am using the shopspring/decimal package and the monetary type is decimal.Decimal - I would like to represent it as either a float or string in my map,

How can I achieve this?

Thank you

https://play.golang.org/p/IVR-c4rGr0Q

How about this?

func StringToDecimalFunc() mapstructure.DecodeHookFunc {
	return func(
		f reflect.Type,
		t reflect.Type,
		data interface{}) (interface{}, error) {
		if f.Kind() != reflect.String {
			return data, nil
		}
		if t != reflect.TypeOf(decimal.Decimal{}) {
			return data, nil
		}
		return decimal.NewFromString(data.(string))
	}
}


func Decode(input interface{}, result interface{}) error {
	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		Metadata: nil,
		DecodeHook: mapstructure.ComposeDecodeHookFunc(
			StringToDecimalFunc()),
		Result: result,
	})
	if err != nil {
		return err
	}

	if err := decoder.Decode(input); err != nil {
		return err
	}
	return err
}

hokaso avatar Apr 25 '22 11:04 hokaso

You need to set the hook in the configuration, for example:

func decode(input, output interface{}) error {
	config := &mapstructure.DecoderConfig{
		DecodeHook: mapstructure.ComposeDecodeHookFunc(
			stringToUUIDHookFunc(),
		),
		Result: &output,
	}

	decoder, err := mapstructure.NewDecoder(config)
	if err != nil {
		return err
	}

	return decoder.Decode(input)
}

func stringToUUIDHookFunc() mapstructure.DecodeHookFunc {
	return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
		if f.Kind() != reflect.String {
			return data, nil
		}
		if t != reflect.TypeOf(uuid.UUID{}) {
			return data, nil
		}

		return uuid.Parse(data.(string))
	}
}

One issue I've noticed, which is specific to https://github.com/google/uuid, is that it is not possible to have mapstructure decode a *uuid.UUID field to nil. This is because uuid.UUID is a [16]byte array, and mapstructure always populates a zero value UUID for *uuid.UUID since the decode function always evaluates case reflect.Array.

However, it would be nice if mapstructure had the ability to handle nil uuid values, rather than always falling through to

	case reflect.Array:
		err = d.decodeArray(name, input, outVal)

silverspace avatar Aug 18 '22 22:08 silverspace

Hello,

Thank you very much for this issue.

How can we use UUID when u decode from struct to map[string]string and the struct has a UUID field. I am getting

err: cannot assign type 'uuid.UUID' to map value field of type 'string'

I noticed the DecodeHook behaves differently in this case. It's not called for each field but only once, which makes it hard to change the data.

Any help will be appreciated.

ioannisGiak89 avatar Jun 03 '24 14:06 ioannisGiak89