4

I have built a database in go with gorm. For this I created a struct and with this struct I created a table. So far so good. In the backend everything works, but in the frontend the problem is that the JSON which is called always returns the ID in upper case and swagger generates me an ID which is lower case. Is there a way in Go that I can overwrite the imported struct from gorm with a JSON identifier?

import "gorm.io/gorm"

type Report struct {
   gorm.Model
   CreatedBy          User     `gorm:"foreignKey:CreatedByUserID" json:"createdBy"`
   Archived           bool     `json:"archived"`
}

This Struct gives me the following response

{
    "ID": 8,
    "CreatedAt": "2022-11-15T20:45:16.83+01:00",
    "UpdatedAt": "2022-12-27T21:34:17.871+01:00",
    "DeletedAt": null
    "createdBy": {
        "ID": 1,
        "CreatedAt": "2022-11-15T20:02:17.497+01:00",
        "UpdatedAt": "2022-11-15T20:02:17.497+01:00",
        ...
    },
    "archived": true,
}

Is there a way to make the ID lowercase (like Archived)? Or can I adjust it at swaggo so that it is generated in upper case.

What I have seen is that you can make the table without this gorm.Model and define all the attributes yourself. The problem is that I then have to create all the functionalities (delete, update, index, primary key, ...) of these columns myself.

mattia_m
  • 119
  • 1
  • 13
  • Did you try embedding ID field with `json:"id"` into Report? You can also try creating another Model which inherits from gorm.Model and add ID in Model, and then inherit Model in Report – Shalom Prinz Jan 02 '23 at 09:37
  • Thanks, I tried to add a `json: "id"` to the `gorm.model`, but then it loaded the attributes for it into a substruct which had the name "id" in the JSON. If I create another struct where I import the attributes from `gorm.model`, then I have the same problem, just one step ahead. Further I don't know how I can overwrite attributes of imported structs in Go". – mattia_m Jan 02 '23 at 10:43

2 Answers2

1

I create my own gorm-model-struct:

type GormModel struct {
    ID        uint           `gorm:"primarykey" json:"id"`
    CreatedAt time.Time      `json:"createdAt"`
    UpdatedAt time.Time      `json:"updatedAt"`
    DeletedAt gorm.DeletedAt `gorm:"index" json:"deletedAt"`
} //@name models.gormModel

I import this struct into the other structs:

type Report struct {
   GormModel
   CreatedBy          User     `gorm:"foreignKey:CreatedByUserID" json:"createdBy"`
   Archived           bool     `json:"archived"`
}

Important is, that you add the json-key and set the attribute name.

mattia_m
  • 119
  • 1
  • 13
0

You can use the mapstructure package.

mapstructure is a Go library for decoding generic map values to structures and vice versa

If you want to embed ID field from gorm.Model with custom json tag, and you want it to be on the same struct and not in a "substruct", you can use mapstructure:",squash" tag on the embedded model.

type Model struct {
    ID uint `json:"id"`
}

type Report struct {
    Model    `mapstructure:",squash"`
    Archived bool `json:"archived"`
}

func main() {
    input := map[string]interface{}{
        "id":       1,
        "archived": false,
    }

    var report Report
    if err := mapstructure.Decode(input, &report); err != nil {
        panic(err)
    }

    fmt.Println("Report ID:", report.ID)
    fmt.Println("Report ID via Model:", report.Model.ID)
}

As you can observe, with mapstructure.Decode method you can convert map to struct with the squash option, and you can then access ID of report directly. Note that you can still access report.Model and all its fields.

With mapstructure, you can make the ID lowercase as you wanted, and also accessable from the report struct directly, not only from a Model substruct.

Shalom Prinz
  • 53
  • 1
  • 5
  • Sorry if I am misunderstanding you. I want to get the ID lowercased in an API request, but I have no influence on the struct which is called `Model` in your case, because this is set by the package. In this package struct it is called `ID` without JSON statement. The data from the database is mapped correctly to the struct. The problem is that I can't access the data correctly in the frontend, because swagger writes the `ID` attribute lowercase to me and therefore I get a lowercase interface generated. In the JSON API response it is capitalized and therefore I can't use it in the frontend. – mattia_m Jan 04 '23 at 20:32
  • I see, I misunderstood you. So for clarification, you don't want to convert it to a struct of your own in the backend, because you would lose some functionalities. Since changing the json tag on a struct is impossible at runtime, I have two ideas for you: 1 - You could convert it to your own struct in the backend just before you send it to the frontend, so you will still be able to use the original struct until you finish using it. 2 - You can use some frontend function on the json you get from the backend before you send it to swaggo. – Shalom Prinz Jan 05 '23 at 22:55
  • I have chosen your first option (slightly changed). It is not the nicest solution, because the ID is now redundant, but otherwise I would have to add all the other attributes created by the `gorm.Model` to ignore it in the swagger. Because I now created another struct `ReportResponse` where I import the report-struct and then added another attribute called `Id` and defined `json: "id"` there for swaggo. Then I created a function that converts the `report` to `reportResponse`. I call this function on the API return, so `c.JSON(http.StatusOK, api.ConvertReportToReportResponse(report))`. – mattia_m Jan 06 '23 at 06:56