1

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\n", 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.

parens
  • 57
  • 1
  • 8

3 Answers3

2

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.

Not_a_Golfer
  • 47,012
  • 14
  • 126
  • 92
  • `json:"a"` etc tags were my cut&paste bad, they are/were actually `redis:"a"` etc. I'm intrigued by your suggestion to "wrap this whole model thing in a struct that does all that logic behind the scene while deserializing from redigo" since that's excatly what I was trying to do, without success. In other words, as you suggest I think, I wanted to go from `{"a":"{'x':'t1', 'y':'t2', 'z':'t3'}"}` being one big string to a.x, a.y, a.z as addressable parts through the struct in one go. I just don't know how. Any tips would be appreciated. Thanks. – parens May 11 '15 at 12:04
  • @parens I made a pull request to redigo that enables what you want :) https://github.com/garyburd/redigo/issues/123 you can use my fork if you want – Not_a_Golfer May 11 '15 at 12:15
  • @parens feel free to voice your opinion on the github issue ;) – Not_a_Golfer May 12 '15 at 07:25
2

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
}
nitimalh
  • 919
  • 10
  • 26
0
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)
Ankit Deshpande
  • 3,476
  • 1
  • 29
  • 42