Golang自定义JSON序列化(是否存在类似于gob.register()的功能来处理JSON?)

huangapple go评论115阅读模式
英文:

Golang custom JSON serialization (does something equivalent to gob.register() exist for json?)

问题

在使用JSON进行编码/解码时,是否有一种方法可以序列化自定义结构体?

假设你有3个(实际代码中有10个)不同的自定义结构体,它们将通过UDP进行发送,并且你使用JSON进行编码:

  1. type a struct {
  2. Id int
  3. Data msgInfo
  4. }
  5. type b struct {
  6. Id int
  7. Data msgInfo
  8. Other metaInfo
  9. }
  10. type c struct {
  11. Other metaInfo
  12. }

在接收端,你想知道接收到的结构体是类型a、b还是c,这样可以将其传递给特定类型的通道。

  1. type msgtype reflect.Type
  2. ...
  3. nrOfBytes, err := udpConn.Read(recievedBytes)
  4. if err != nil {...}
  5. var msg interface{}
  6. err = json.Unmarshal(recievedBytes[0:nrOfBytes], &msg)
  7. if err != nil {...}
  8. u := reflect.ValueOf(msg)
  9. msgType := u.Type()
  10. fmt.Printf("msg is of type: %s\n", msgType)

使用gob可以很容易地实现这一点,只需注册类型即可。但由于使用UDP进行通信,所以必须使用JSON,那么有没有办法序列化自定义结构体呢?我希望打印结果是:

  1. msg is of type: a

但实际上我得到的是:

  1. 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:

  1. type a struct {
  2. Id int
  3. Data msgInfo
  4. }
  5. type b struct {
  6. Id int
  7. Data msgInfo
  8. Other metaInfo
  9. }
  10. type c struct {
  11. Other metaInfo
  12. }

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.

  1. type msgtype reflect.Type
  2. .
  3. .
  4. nrOfBytes, err := udpConn.Read(recievedBytes)
  5. if err != nil {...}
  6. var msg interface{}
  7. err = json.Unmarshal(recievedBytes[0:nrOfBytes], &msg)
  8. if err != nil {...}
  9. u := reflect.ValueOf(msg)
  10. msgType := u.Type()
  11. 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

  1. msg is of type: a

but i'm only getting

  1. msg is of type: map[string]interface {}

答案1

得分: 2

你可以使用json.RawMessage类型和自定义的Wrapper类型来实现。然后,在接收到消息时,你可以使用switch语句(或使用构造函数的映射)来获取正确的结构体。

例如(省略错误检查):

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. )
  6. type Message struct {
  7. Type string
  8. Data json.RawMessage
  9. }
  10. func (m Message) Struct() interface{} {
  11. unmarshaller, found := unmarshallers[m.Type]
  12. if !found {
  13. return nil
  14. }
  15. return unmarshaller([]byte(m.Data))
  16. }
  17. type Foo struct {
  18. ID int
  19. }
  20. var unmarshallers = map[string]func([]byte) interface{}{
  21. "foo": func(raw []byte) interface{} {
  22. var f Foo
  23. json.Unmarshal(raw, &f)
  24. return f
  25. },
  26. }
  27. func main() {
  28. var body = []byte(`{"Type":"foo","Data":{"ID":1}}`)
  29. var msg Message
  30. json.Unmarshal(body, &msg)
  31. switch s := msg.Struct().(type) {
  32. case Foo:
  33. fmt.Println(s)
  34. }
  35. }

你可以在这个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):

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. )
  6. type Message struct {
  7. Type string
  8. Data json.RawMessage
  9. }
  10. func (m Message) Struct() interface{} {
  11. unmarshaller, found := unmarshallers[m.Type]
  12. if !found {
  13. return nil
  14. }
  15. return unmarshaller([]byte(m.Data))
  16. }
  17. type Foo struct {
  18. ID int
  19. }
  20. var unmarshallers = map[string]func([]byte) interface{}{
  21. "foo": func(raw []byte) interface{} {
  22. var f Foo
  23. json.Unmarshal(raw, &f)
  24. return f
  25. },
  26. }
  27. func main() {
  28. var body = []byte(`{"Type":"foo","Data":{"ID":1}}`)
  29. var msg Message
  30. json.Unmarshal(body, &msg)
  31. switch s := msg.Struct().(type) {
  32. case Foo:
  33. fmt.Println(s)
  34. }
  35. }

See this playground example for a live demo http://play.golang.org/p/7FmQqnWPaE

答案2

得分: 0

根据你的评论,这可能是一个解决方案:

  1. type Packet struct {
  2. Type string
  3. Data []byte
  4. }
  5. // 编码函数
  6. func packageEncode(i interface{}) ([]byte, error) {
  7. b, err := json.Marshal(i)
  8. if err != nil {
  9. return nil, err
  10. }
  11. p := &Packet{Data: b}
  12. switch t := i.(type) {
  13. case a:
  14. p.Type = "a"
  15. case b:
  16. p.Type = "b"
  17. case c:
  18. p.Type = "c"
  19. }
  20. return json.Marshal(p)
  21. }
  22. // 当接收到消息并解码时
  23. p := &Packet{}
  24. err = json.Unmarshal(recievedBytes[0:nrOfBytes], p)
  25. ...
  26. if p.Type == "a" {
  27. msg := &a{}
  28. err = json.Unmarshal(p.Data, msg)
  29. }
  30. if p.Type == "b" {
  31. ...
  32. }

请注意,我已经将代码中的&替换为了正确的&符号。

英文:

Base on your comment, this maybe a solution:

  1. type Packet {
  2. Type string
  3. Data []byte
  4. }

Encode function:

  1. func packageEncode(i interface{}) ([]byte, error) {
  2. b, err := json.Marshal(i)
  3. if err != nil {
  4. return nil, err
  5. }
  6. p := &Package{Data: b}
  7. switch t.(type) {
  8. case a:
  9. p.Type = "a"
  10. case b:
  11. p.Type = "b"
  12. case c:
  13. p.Type = "c"
  14. }
  15. return json.Marshal(p)
  16. }

Then, when recieved message and decode:

  1. p := &Package{}
  2. err = json.Unmarshal(recievedBytes[0:nrOfBytes], p)
  3. ...
  4. if p.Type == "a" {
  5. msg := &a{}
  6. err = json.Unmarshal(p.Data, msg)
  7. }
  8. if p.Type == "b" {
  9. ...
  10. }

huangapple
  • 本文由 发表于 2016年4月7日 22:23:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/36479128.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定