反序列化嵌套 JSON,或者简单地在 Go 中将其向前传递

Deserializing nested JSON, or simply pass it forward in Go

使用 Go 构建一个基本的 API,我已经将 JSON 存储在 postgres table 的 JSON 字段中,以及其他一些(普通)数据类型。使用我的模型,我只是想从数据库中获取一行并将其作为 JSON.

向前传递

使用 GORM 将数据反序列化为结构,大部分映射都是无缝发生的,除了 JSON,它根据所选数据类型呈现为字节数组或字符串。

以下是模型(更新):

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"`
}

典型的 JSON 看起来像这样(来自数据库):

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

所以我的想法是我只是想传递这个数据,而不是改变它,或者将它反序列化为 Go 结构或任何东西。 controller/route 如下:

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)
    }
}

响应如下 JSON(统计数据为 json.RawMessage):

{

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

或者(将统计数据作为字符串):

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

我有什么选项可以简单地传递它,到目前为止,我没有成功地尝试将 JSON 映射到结构(由于动态数据,这变得困难,以及我选择 [的原因=45=] 开头)?

我意识到 gin-gonic 发生了一些神奇的事情,c.JSON 自动(?)将结构中的所有数据编组到 JSON,但希望有一些方法可以避免编组 json 数据?

当 运行 使用 ItemInfo 子结构时,它会出现以下错误:

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)

编辑:更新代码:

创建一个像 itemInfo 或类似的子结构:

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

然后在你的 Item 结构中 make

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 看看我是如何完成的,但它完全未完成我只实现了我在做某事时需要的部分。

事实证明,最简单的方法是在 Item 结构中提供额外的 属性 以便 GORM 解组为 []byte,然后将字节数组解组为 sub-struct:

// 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 的建议。