英文:
Casting json parts into Go structs from Redis using redigo
问题
我有一个由浏览器POST的JSON数据。它是一个哈希值,包含一个名为id的int64变量和三个字段。示例代码如下:
myJson := `{
"a":"{'x1':'apple','x2':'orange'}",
"b":"{'y1':345678,'y2':32456}",
"c":"['alpha@example1.com', 'beta@example2.com']"
}`
然后,使用**redigo**将其存储在Redis中,使用以下命令:
HMSET id:123 a "{'x1':'apple','x2':'orange'}" b "{'y1':345678,'y2':32456}" c "['alpha@example1.com', 'beta@example2.com']"
现在,我想在Go中使用以下模型:
type Model struct {
A string `json:"a"`
B string `json:"b"`
C string `json:"c"` // 运行时未知长度的映射
}
1. 我使用以下代码调用Redis:
v, _ := redis.Values(c.Do("HGETALL", "id:123"))
我可以通过redis-cli正确查看存储的值,但是将v回复转换为Model结构体时出现问题:
var model Model
if err := redis.ScanStruct(v, &model); err != nil {
panic(err)
}
fmt.Printf("c %#v\n", model.C) => 空 []
我想访问单独的键值对,例如:
B.y2 = 32456
C[0] = "alpha@example1.com"
2. 我还想将myJson作为组合的形式(如{a},{a,b},{a,c},{a,b,c}等)通过json.Marshal返回给浏览器。我不确定如何将各种a、b、c字段组合成一个进行编组。
希望能得到帮助。
英文:
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.
答案1
得分: 2
首先,你应该使用redis
标签而不是json
标签来标记你的字段名称,这是redigo在ScanStruct()中使用的方式。例如:
type Model struct {
A string `redis:"a"`
B string `redis:"b"`
C string `redis:"c"` // 运行时未知长度的映射
}
其次,你的成员是字符串类型,所以无法直接访问它们的内容,并且我认为你无法使用redigo自动化地实现这一点。
作为一种解决方法,你可以创建一个扩展了字符串类型的自定义类型,并具有一个访问方法,该方法会将JSON解析为底层字典,然后返回值。类似这样的实现(没有经过测试,只是一个大致的想法,不确定是否可行,但值得一试):
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
}
这种方法效率也不是很高,因为每次都会重新解析JSON。个人建议将整个模型封装在一个结构体中,在从redigo反序列化时在后台执行所有这些逻辑。
英文:
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.
答案2
得分: 2
请查看RedisLabs的ReJSON模块。
我在这里为它创建了一个简单的Go客户端链接,可以与Redigo一起使用。
type Model struct {
A string `redis:"a" json:"a"`
B string `redis:"b" json:"b"`
C string `redis:"c" json:"c"`
}
要读取它,请使用JSON.GET
命令:
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
}
英文:
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
}
答案3
得分: 0
你可以同时使用redis
标签和json
标签。ScanStruct
应该可以正常工作,它使用redis
标签。
type Model struct {
A string `redis:"a" json:"a"`
B string `redis:"b" json:"b"`
C string `redis:"c" json:"c"`
}
m := Model{}
v, err := redis.Values(c.Do("HGETALL", key))
err = redis.ScanStruct(v, &m)
这段代码定义了一个Model
结构体,其中包含了三个字段A
、B
和C
,它们分别使用了redis
和json
标签。在使用ScanStruct
函数时,可以将返回的redis.Values
类型的结果与m
进行绑定,实现将结果扫描到结构体中的字段中。
英文:
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.
<br> ScanStruct should work fine, it uses redis
tags.<br>
m := Model
v, err := redis.Values(c.Do("HGETALL", key))
err = redis.ScanStruct(v, &m)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论