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

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

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" {
  ...
}

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:

确定