-
Notifications
You must be signed in to change notification settings - Fork 193
Tips and Tricks
Different implementations of MessagePack deal with numbers differently, partly as a consequence of how each source language expresses integers. (Some languages, like Ruby, do not have unsigned integers, while others, like JavaScript, have no integers at all. Meanwhile, Go, C, and C++ all support signed and unsigned integers up to machine word size as distinct types.)
msgp
has a built-in type called msgp.Number
that can represent a MessagePack int
, uint
, float32
, and float64
. If you are decoding objects serialized by other libraries into Go objects using msgp
, you may have to use msgp.Number
instead of a builtin numeric type.
Much like encoding/json
, msgp
has a type called msgp.Raw
that represents serialized MessagePack. Among other things, you can use msgp.Raw
for containers and polymorphism. Take the following, for example:
type Data struct {
Header map[string]string `msg:"header"`
Body msgp.Raw `msg:"body"`
}
The Data
object could reasonably be used as a container for any MessagePack object. Later, if you want to de-serialize a concrete object from the raw data, you would simply call (*msgp.Unmarshaler).UnmarshalMsg([]byte(msgp.Raw))
.
package main
import (
"fmt"
"github.com/tinylib/msgp/msgp"
)
//go:generate msgp
type Foo struct {
A string `msg:"a"`
Another string `msg:"b"`
}
type Data struct {
Header uint16 `msg:"header"`
Body msgp.Raw `msg:"body"`
}
func main() {
foo1 := Foo{A: "Hello", Another: "World"}
body, _ := foo1.MarshalMsg(nil)
fmt.Printf("foo1 is encoded as %x\n", body)
data := Data{
Header: 1,
Body: body,
}
dataPacket, _ := data.MarshalMsg(nil)
var (
readPacket Data
readFoo Foo
)
_, _ = readPacket.UnmarshalMsg(dataPacket)
if readPacket.Header == 1 {
_, _ = readFoo.UnmarshalMsg([]byte(readPacket.Body))
fmt.Println(readFoo)
}
}
Generating methods for a type defined outside the package in which you are currently working presents some challenges, as Go does not support defining methods for a type outside its originating package. There are, however, some workarounds:
- You can specify
-file
in your//go:generate
directive to be a foreign file (located somewhere else in your GOPATH), which will generate methods in the foreign package directly. Distributing such code would probably require maintaining a fork and/or git submodule. - You can define an identical
struct
type in your own package, and pointer-cast from the old type to the one defined in your package when you need to serialize.
All of the errors returned by the msgp
package implement the msgp.Error
interface. Thus, it is simple to distinguish between errors returned from encoding/decoding versus errors returned from other sources. Additionally, msgp
errors will tell you whether or not the error allows the user to continue reading/writing to a stream. For example:
f, err := r.ReadFloat64()
if err != nil {
if msgerr, ok := err.(msgp.Error); ok && msgerr.Resumable() {
// the next object probably isn't a float64
}
// something else went wrong
log.Fatalln("something broke:", err)
}
See [Zero Values and Omitempty] for information on how to omit empty (zero value) struct members from the emitted message package output.