mapstructure icon indicating copy to clipboard operation
mapstructure copied to clipboard

Source data must be an array or slice, got string

Open bbmeme05 opened this issue 3 years ago • 2 comments

When i try decode a map[string]interface {} into stuct the error return "Source data must be an array or slice, got string". This is the code :

func main() {
	id, _ := primitive.ObjectIDFromHex("62295b0650935306baee588c")
	source := &Person{
		Name: "Lucy",
		Data: &User{
			Username: "Luck",
			Uid:      id,
		},
	}
	bytes, _ := json.Marshal(source)
	target := &Person{}
	json.Unmarshal(bytes, target)
	fmt.Printf("%T\n", target.Data)
	result := &User{}
	config := mapstructure.DecoderConfig{
		Result:  result,
		TagName: "json",
	}
	decoder, _ := mapstructure.NewDecoder(&config)
	err := decoder.Decode(target.Data)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("%+v", result)
}

type User struct {
	Username string             `json:"username"`
	Uid      primitive.ObjectID `json:"uid"`
}

type Person struct {
	Name string `json:"name"`
	Data any    `json:"data"`
}

The error message is "* 'uid': source data must be an array or slice, got string"

bbmeme05 avatar Oct 19 '22 06:10 bbmeme05

I think that's because the primitive.ObjectID type is a an array of byte but when it decode from map the given type was string.

ArkjuniorK avatar Feb 09 '23 04:02 ArkjuniorK

You could add DecodeHook with given syntax, it would check the type both of destination type and origin type since ObjectID is an array we set the destination type to array if it match then convert the data to string and generate new ObjectID with it. Hopes it help!

DecodeHook: func(from, to reflect.Type, d interface{}) (interface{}, error) {
	if from.Kind() == reflect.String && to.Kind() == reflect.Array {
		sid := d.(string)
		id, err := primitive.ObjectIDFromHex(sid)
		if err != nil {
			fmt.Println(err)
		}

		d = id
	}

	return d, nil
},

ArkjuniorK avatar Feb 09 '23 05:02 ArkjuniorK