I have a json that is POSTed by a browser. It's a hash with
var id int64 = 123
and, say, three fields like so:
myJson := `{
"a":"{'x1':'apple','x2':'orange'}",
"b":"{'y1':345678,'y2':32456}",
"c":"['alpha@example1.com', 'beta@example2.com']"}`
It's then stored in Redis using redigo with command:
HMSET id:123 a "{'x1':'apple','x2':'orange'}" b "{'y1':345678,'y2':32456}" c "['alpha@example1.com', 'beta@example2.com']"
Now, I'd like to use a model like this in Go
type Model struct {
A string `json:"a"`
B string `json:"b"`
C string `json:"c"` // Unknown length of map at runtime
}
1. I call Redis with
v, _ := redis.Values(c.Do("HGETALL", "id:123"))
I see correctly stored values via redis-cli, but converting the v reply into the Model struct doesn't work:
var model Model
if err := redis.ScanStruct(v, &model); err != nil {
panic(err)
}
fmt.Printf("c %#v
", model.C) => empty []
I'd like to access individual k:v pairs like:
B.y2 = 32456
C[0] = "alpha@example1.com"
2. I'd also like to json.Marshal myJson back to the browser as combinations of {a}, {a,b}, {a,c}, {a,b,c}, etc. I'm not sure how to combine various a,b,c field combos into one to be Marshalled.
Any help would be appreciated.
First of all, you should tag your field names with redis
and not json
tags, that's what redigo uses for ScanStruct(). e.g
type Model struct {
A string `redis:"a"`
B string `redis:"b"`
C string `redis:"c"` // Unknown length of map at runtime
}
Second, your members are strings, so you can't have individual member access to their content, and I don't think you can automate it with redigo.
You can, as workaround have some type that extends string, and has an access method that lazily parses the json into an underlying dict and then returns the value. Something like this (writing without testing, just the general idea, I'm not sure it will work but it's worth a try):
type MyString string
func (s MyString)Get(key string) (interface{}, error) {
var m map[string]interface{}
if err := json.Unmarshal([]byte(s), &m); err != nil {
return nil, err
}
return m[key], nil
}
It's also not very efficient as it will parse the json again each time. Personally I'd wrap this whole model thing in a struct that does all that logic behind the scene while deserializing from redigo.
type Model struct {
A string `redis:"a" json:"a"`
B string `redis:"b" json:"b"`
C string `redis:"c" json:"c"`
}
You can use redis
tags with json
tags at the sametime.
ScanStruct should work fine, it uses redis
tags.
m := Model
v, err := redis.Values(c.Do("HGETALL", key))
err = redis.ScanStruct(v, &m)
Check out the ReJSON module from RedisLabs.
I created a simple go-client for it here that works with Redigo.
type Model struct {
A string `redis:"a" json:"a"`
B string `redis:"b" json:"b"`
C string `redis:"c" json:"c"`
}
To read it back use the JSON.GET
command,
v, err := redis.Bytes(rejson.JSONGet(conn, "id:123", ""))
if err != nil {
return
}
m := new(Model)
err = json.Unmarshal(v, m)
if err != nil {
return
}