英文:
Golang custom JSON serialization (does something equivalent to gob.register() exist for json?)
问题
在使用JSON进行编码/解码时,是否有一种方法可以序列化自定义结构体?
假设你有3个(实际代码中有10个)不同的自定义结构体,它们将通过UDP进行发送,并且你使用JSON进行编码:
type a struct {
Id int
Data msgInfo
}
type b struct {
Id int
Data msgInfo
Other metaInfo
}
type c struct {
Other metaInfo
}
在接收端,你想知道接收到的结构体是类型a、b还是c,这样可以将其传递给特定类型的通道。
type msgtype reflect.Type
...
nrOfBytes, err := udpConn.Read(recievedBytes)
if err != nil {...}
var msg interface{}
err = json.Unmarshal(recievedBytes[0:nrOfBytes], &msg)
if err != nil {...}
u := reflect.ValueOf(msg)
msgType := u.Type()
fmt.Printf("msg is of type: %s\n", msgType)
使用gob可以很容易地实现这一点,只需注册类型即可。但由于使用UDP进行通信,所以必须使用JSON,那么有没有办法序列化自定义结构体呢?我希望打印结果是:
msg is of type: a
但实际上我得到的是:
msg is of type: map[string]interface {}
英文:
Is there a way to serialize custom structs when encoding/decoding with json?
say you have 3 (in my actual code there are 10) different custom structs which are being sent over udp, and you use json for encoding:
type a struct {
Id int
Data msgInfo
}
type b struct {
Id int
Data msgInfo
Other metaInfo
}
type c struct {
Other metaInfo
}
On the recieving end you want to know if the struct recieved was of type a, b or c, so it can for example be passed to a type spesific channel.
type msgtype reflect.Type
.
.
nrOfBytes, err := udpConn.Read(recievedBytes)
if err != nil {...}
var msg interface{}
err = json.Unmarshal(recievedBytes[0:nrOfBytes], &msg)
if err != nil {...}
u := reflect.ValueOf(msg)
msgType := u.Type()
fmt.Printf("msg is of type: %s\n", msgType)
With gob this is easily done by registering the types, but i have to use json seeing as it's communication over udp, so is there anyway to serialize the custom structs? I want the print to be
msg is of type: a
but i'm only getting
msg is of type: map[string]interface {}
答案1
得分: 2
你可以使用json.RawMessage
类型和自定义的Wrapper类型来实现。然后,在接收到消息时,你可以使用switch语句(或使用构造函数的映射)来获取正确的结构体。
例如(省略错误检查):
package main
import (
"encoding/json"
"fmt"
)
type Message struct {
Type string
Data json.RawMessage
}
func (m Message) Struct() interface{} {
unmarshaller, found := unmarshallers[m.Type]
if !found {
return nil
}
return unmarshaller([]byte(m.Data))
}
type Foo struct {
ID int
}
var unmarshallers = map[string]func([]byte) interface{}{
"foo": func(raw []byte) interface{} {
var f Foo
json.Unmarshal(raw, &f)
return f
},
}
func main() {
var body = []byte(`{"Type":"foo","Data":{"ID":1}}`)
var msg Message
json.Unmarshal(body, &msg)
switch s := msg.Struct().(type) {
case Foo:
fmt.Println(s)
}
}
你可以在这个playground示例中查看实时演示:http://play.golang.org/p/7FmQqnWPaE
英文:
One thing you can do is using the json.RawMessage
type and a custom Wrapper type.
Then, upon receiving a message, you can do a switch (or use a map of constructors) to get the right struct.
For example (omitting error checking):
package main
import (
"encoding/json"
"fmt"
)
type Message struct {
Type string
Data json.RawMessage
}
func (m Message) Struct() interface{} {
unmarshaller, found := unmarshallers[m.Type]
if !found {
return nil
}
return unmarshaller([]byte(m.Data))
}
type Foo struct {
ID int
}
var unmarshallers = map[string]func([]byte) interface{}{
"foo": func(raw []byte) interface{} {
var f Foo
json.Unmarshal(raw, &f)
return f
},
}
func main() {
var body = []byte(`{"Type":"foo","Data":{"ID":1}}`)
var msg Message
json.Unmarshal(body, &msg)
switch s := msg.Struct().(type) {
case Foo:
fmt.Println(s)
}
}
See this playground example for a live demo http://play.golang.org/p/7FmQqnWPaE
答案2
得分: 0
根据你的评论,这可能是一个解决方案:
type Packet struct {
Type string
Data []byte
}
// 编码函数
func packageEncode(i interface{}) ([]byte, error) {
b, err := json.Marshal(i)
if err != nil {
return nil, err
}
p := &Packet{Data: b}
switch t := i.(type) {
case a:
p.Type = "a"
case b:
p.Type = "b"
case c:
p.Type = "c"
}
return json.Marshal(p)
}
// 当接收到消息并解码时
p := &Packet{}
err = json.Unmarshal(recievedBytes[0:nrOfBytes], p)
...
if p.Type == "a" {
msg := &a{}
err = json.Unmarshal(p.Data, msg)
}
if p.Type == "b" {
...
}
请注意,我已经将代码中的&
替换为了正确的&
符号。
英文:
Base on your comment, this maybe a solution:
type Packet {
Type string
Data []byte
}
Encode function:
func packageEncode(i interface{}) ([]byte, error) {
b, err := json.Marshal(i)
if err != nil {
return nil, err
}
p := &Package{Data: b}
switch t.(type) {
case a:
p.Type = "a"
case b:
p.Type = "b"
case c:
p.Type = "c"
}
return json.Marshal(p)
}
Then, when recieved message and decode:
p := &Package{}
err = json.Unmarshal(recievedBytes[0:nrOfBytes], p)
...
if p.Type == "a" {
msg := &a{}
err = json.Unmarshal(p.Data, msg)
}
if p.Type == "b" {
...
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论