mitchellh / mapstructure

Go library for decoding generic map values into native Go structures and vice versa.
https://gist.github.com/mitchellh/90029601268e59a29e64e55bab1c5bdc
MIT License
7.93k stars 677 forks source link

Lowercase keys of resulting map without tags #259

Closed UrbanMarco closed 2 years ago

UrbanMarco commented 3 years ago

I'm using gorm v1 as an ORM and gomocket as a mocking library. I want to be able to pass any structure to the mock response creator function and get back the matching mock. This is a simplified example:

import (
    "github.com/mitchellh/mapstructure"
    mocket "github.com/selvatico/go-mocket"
)

type ComplexObj struct {
    Name string
    Age int
    //... and so on
}

func GetMockCall(obj ComplexObj) *mocket.FakeResponse {
    response := []map[string]interface{}{}

    var serializedObj map[string]interface{}
    if err := mapstructure.Decode(obj, &serializedObj); err != nil {
        panic(err)
    }
    response = append(response, serializedObj)
    return &mocket.FakeResponse{
        Pattern:  `SELECT * FROM "table""`,
        Response: response,
    }
}

The problem I have is that the resulting serializedObj has the keys uppercased, exactly as the initial structure ComplexObj. This is a problem for gorm, because, according to the docs, the keys must be snake_cased. So when calling the mock, the result set will contain the right elements, but the values will be lost (because gorm is expecting lowercase/snake_case keys).

Is there a way to make this conversion during the Decode process? I believe the DecodeHook might help, but I'm completely clueless about reflections and so I'm not sure how this should be coded. Can we get an example, or a starting point at least?

UrbanMarco commented 3 years ago

On a further note: using tags to change the casing of the keys in the output map is not ideal because, in case anyone has a struct containing embedded structures coming from external dependencies (like in the gorm.Model), the inner elements contained inside the embedded structure will not be snake-cased.

Example here: https://play.golang.org/p/qf4mrj9vzY4

mitchellh commented 2 years ago

There isn't a magic way to do this and I'm not sure if mapstructure should do this.

I'd recommend just iterating over the map after and lowercasing. A decode hook can potentially do this yes but I'm unsure. Another approach is to look at libraries like reflectwalk (but that's kind of a big hammer for this problem).