Search code examples
gounmarshalling

Why Json Unmarshall changing array type in Golang?


I'm trying to seed my Postgres database as functionally. In my case, SeedSchema() function can take any type struct. So I define a interface and create functions to my structs which will seed. I tried with generics and without.

When I unmarshall any json array from file as byte array, json.Unmarshall method change my tempMember member of struct. Exp, models.Term to map[string]interface{}. I've used unmarshall before this function and I've not seen like this situation.

Here is my SeedSchema() function:

func (db *Database) SeedSchema(models ...globals.Seeder[any]) error {
var (
    subjects []globals.Seeder[any]
    fileByte []byte
    err      error
    // tempMember any
)
if len(models) == 0 {
    subjects = seederModelList
} else {
    subjects = models
}
for _, model := range subjects {
    fileName, tempMember := model.Seed()
    fmt.Printf("%+v\n", reflect.TypeOf(tempMember)) //1
    if fileByte, err = os.ReadFile("db/seeds/" + fileName); err != nil {
        fmt.Println(err)
        return err
    }
    if err = json.Unmarshal(fileByte, &tempMember); err != nil {
        fmt.Println(err)
        return err
    }
    fmt.Printf("%+v\n", reflect.TypeOf(tempMember)) //2

}
return nil 
}

First print returns []models.AirportCodes and the second []interface {}.

Here is my interface and model:

func (AirportCodes) Seed() (string, any) {
    return "airport_codes.json", []AirportCodes{}
}

type Seeder[T any] interface {
    Seed() (string, T)
    // Seed(*gorm.DB) error
    TableName() string
}

seederModelList = []globals.Seeder[any]{
        m.AirportCodes{},
        m.Term{},
    }


Solution

  • After a few weeks, I have looking for solve this problem and look unmarshaler interfaces and examples. Then Like what icza said, I started to look over the my code that convention between types and I solved like this. If you guys have better answer than mine, please add answer.

    Data:

    [
        {
            "id":1,
            "name":"Term 1",
            "content": [
                "a1",
                "a2",
                "a3"
            ]
        }
    ]
    

    Result:

    [{ID:1 Name:Term 1 Content:[a1 a2 a3]}]
    

    UnmarshalJSON Function:

    func (term *Term) UnmarshalJSON(data []byte) error {
        tempMap := map[string]interface{}{}
        if err := json.Unmarshal(data, &tempMap); err != nil {
            return err
        }
        *term = Term{
            Name: tempMap["name"].(string),
        }
        if tempMap["content"] != nil {
            for _, v := range tempMap["content"].([]interface{}) {
                (*term).Content = append((term).Content, v.(string))
            }
        }
        return nil
    }
    
    

    Thank you for comments.