0

Building a basic API with Go, I've got JSON stored in a JSON field in a postgres table, along with some other (plain) datatypes. Using my model, I'm simply trying to fetch a row from the database and pass it forward as JSON.

Using GORM to deserialize the data into a struct, most of the mapping happens seamlessly, except for the JSON, which depending on selected datatype either renders as a bytearray or string.

Here are the models (Updated):

type Item struct {
    --snip--
    Stats []ItemInfo `gorm:"column:stats" json:"stats" sql:"json"`
    --snip--
}

type ItemInfo struct {
    Stat        string      `json:"stat"`
    Amount      int         `json:"amount"`
}

With the typical JSON looking like this (from the DB):

[{"stat": "Multistrike", "amount": 193}, {"stat": "Crit", "amount": 145}, 
 {"stat": "Agility", "amount": 254}, {"stat": "Stamina", "amount": 381}]

So the idea is that I simply want to pass this data on, not alter it, or deserialize it to a Go struct or anything. The controller/route follows:

func GetItem(c *gin.Context) {
    id, err := strconv.Atoi(c.Param("id"))

    // Convert Parameter to int, for db query
    if err != nil {
        panic(err)
    }

    // Get the DB context
    db, ok := c.MustGet("databaseConnection").(gorm.DB)
    if !ok {
        // Do something
    }

    // Hold the structified item here.
    var returnedItem models.Item

    // Get the db row
    db.Where(&models.Item{ItemID: id}).First(&returnedItem)

    if c.Bind(&returnedItem) == nil {

        // Respond with the struct as json
        c.JSON(http.StatusOK, returnedItem)
    }
}

Which responds with the following JSON (with stats as json.RawMessage):

{

    "context": "raid-finder",
    "stats": "W3sic3RhdCI6ICJWZXJzYXRpbGl0eSIsICJhbW91bnQiOiA0NX0sIHsic3RhdCI6ICJDcml0IiwgImFtb3VudCI6IDEwMH0sIHsic3RhdCI6ICJBZ2lsaXR5IiwgImFtb3VudCI6IDEwOX0sIHsic3RhdCI6ICJTdGFtaW5hIiwgImFtb3VudCI6IDE2M31d",
}

Or alternatively (with stats as string):

{
    "context": "raid-finder",
    "stats": "[{\"stat\": \"Versatility\", \"amount\": 45}, {\"stat\": \"Crit\", \"amount\": 100}, {\"stat\": \"Agility\", \"amount\": 109}, {\"stat\": \"Stamina\", \"amount\": 163}]",
}

What options do I have to simply pass this on, so far I've unsuccessfully attempted to map the JSON to a struct (which becomes difficult because of the dynamic data, and the reason I chose JSON to start with)?

I realize there's some magic going on from gin-gonic, with c.JSON automatically(?) marshalling all of the data from the struct to JSON, but hoping there's some way to avoid marshalling the json data?

When ran with the ItemInfo substruct, it panics with the following error:

2016/01/07 08:21:08 Panic recovery -> reflect.Set: value of type []uint8 is not assignable to type []models.ItemInfo
/usr/local/go/src/runtime/panic.go:423 (0x42a929)
        gopanic: reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
/usr/local/go/src/reflect/value.go:2158 (0x5492ce)
        Value.assignTo: panic(context + ": value of type " + v.typ.String() + " is not assignable to type " + dst.String())
/usr/local/go/src/reflect/value.go:1327 (0x546195)

EDIT: Updated code:

4

2 回答 2

0

itemInfo制作一个类似或类似的子结构:

type itemInfo struct {
    Stat string `json:"stat"`
    Crit int    `json:"crit"`
}

然后在你的Item结构中

type Item struct {
    --snip--
    Context string `gorm:"column:context" json:"context"`
    Stats []itemInfo `gorm:"column:stats" json:"stats" sql:"json"`
    --snip--
}

然后,当您解组时,它应该可以很好地进入项目信息!

另外我假设你正在使用暴雪 API,我已经制作了一个包装器,你可以在这里查看它:https ://github.com/Gacnt/Go-WoW-API看看我是如何做到的,但它完全是未完成我只实现了我在做某事时需要的部分。

于 2016-01-06T00:13:25.840 回答
0

事实证明,最简单的方法是在 Item 结构中为 GORM 提供一个附加属性来解组为 []byte,然后将字节数组解组为子结构:

// Item is a thing..
type Item 
    Stats           []byte    `gorm:"column:stats"  json:"stats"`
    StatsList       []ItemInfo `json:"iteminfo"`
}

并像这样解组它:

err = json.Unmarshal(returnedItem.Stats, &returnedItem.StatsList)

感谢@evanmcdonnal 的建议。

于 2016-01-07T22:43:12.570 回答