Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

How to use mapstructure with google uuid #236

Open
eloo opened this issue Jan 27, 2021 · 8 comments
Open

How to use mapstructure with google uuid #236

eloo opened this issue Jan 27, 2021 · 8 comments

Comments

@eloo
Copy link

eloo commented Jan 27, 2021

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

@alex-rufo
Copy link

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))
	}
}

@eloo
Copy link
Author

eloo commented Jan 28, 2021

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

@alex-rufo
Copy link

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))
	}
}

@eloo
Copy link
Author

eloo commented Jan 29, 2021

@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?

@asarandi
Copy link

asarandi commented Feb 5, 2021

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

@hokaso
Copy link

hokaso commented Apr 25, 2022

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
}

@silverspace
Copy link

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)

@ioannisGiak89
Copy link

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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants