dtvp3625
dtvp3625
2016-01-05 19:19
浏览 60

反序列化嵌套JSON,或直接将其转发到Go中

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:

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

2条回答 默认 最新

  • duanluwei9374
    duanluwei9374 2016-01-07 22:43
    已采纳

    Turns out it was easiest to just provide an additional property in the Item struct for GORM to unmarshal into []byte and then unmarshal the byte array as the sub-struct:

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

    And unmarshal it like this:

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

    Thanks to @evanmcdonnal for the suggestion.

    点赞 评论
  • duanchuli5647
    duanchuli5647 2016-01-06 00:13

    Make a sub struct like itemInfo or something like:

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

    Then in your Item struct make

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

    Then when you unmarshal it should go just fine into item info!

    Also I'm assuming you're using blizzards API, I have made a wrapper already you can view it here: https://github.com/Gacnt/Go-WoW-API to see how I have done it, but it's completely unfinished I only implemented the parts I needed when I was working on something.

    点赞 评论

相关推荐