MoshiPack
Gradle
implementation com.daveanthonythomas.moshipack:moshipack:1.0.1
Optional Retrofit support:
implementation com.daveanthonythomas.moshipack:moshipack-retrofit:1.0.1
About
This is a Kotilin implementation of MessagePack serialization and deserialization built ontop of Moshi to take advantage of Moshi's type adapters and utilizes okio for reading and writing MessagePack bytes.
The library is intended to be consumed in a Kotlin project, and is not intended for Java use.
Inspired by Kaushik Gopal's tweet
See Moshi for adapter usage and reference.
Convert an object to MessagePack format
data class MessagePackWebsitePlug(var compact: Boolean = true, var schema: Int = 0)
val moshiPack = MoshiPack()
val packed: BufferedSource = moshiPack.pack(MessagePackWebsitePlug())
println(packed.readByteString().hex())
This prints the MessagePack bytes as a hex string 82a7636f6d70616374c3a6736368656d6100
- 82 - Map with two entries
- a7 - String of seven bytes
- 63 6f 6d 70 61 63 74 - UTF8 String "compact"
- c3 - Boolean value true
- a6 - String of size bytes
- 73 63 68 65 6d 61 - UTF8 String "schema"
- 00 - Integer value 0
Convert binary MessagePack back to an Object
val bytes = ByteString.decodeHex("82a7636f6d70616374c3a6736368656d6100").toByteArray()
val moshiPack = MoshiPack()
val plug: MessagePackWebsitePlug = moshiPack.unpack(bytes)
Static API
If you prefer to not instantiate a MoshiPack
instance you can access the API in a static fashion as well. Note this will create a new Moshi
instance every time you make an API call. You may want to use the API this way if you aren't providing MoshiPack
by some form of dependency injection and you do not have any specific builder parameters for Moshi
Format Support
See MessagePack format spec for further reference.
format name | first byte (in binary) | first byte (in hex) | Supported |
---|---|---|---|
positive fixint | 0xxxxxxx | 0x00 - 0x7f | Yes |
fixmap | 1000xxxx | 0x80 - 0x8f | Yes |
fixarray | 1001xxxx | 0x90 - 0x9f | Yes |
fixstr | 101xxxxx | 0xa0 - 0xbf | Yes |
nil | 11000000 | 0xc0 | Yes |
(never used) | 11000001 | 0xc1 | Yes |
false | 11000010 | 0xc2 | Yes |
true | 11000011 | 0xc3 | Yes |
bin 8 | 11000100 | 0xc4 | No |
bin 16 | 11000101 | 0xc5 | No |
bin 32 | 11000110 | 0xc6 | No |
ext 8 | 11000111 | 0xc7 | No |
ext 16 | 11001000 | 0xc8 | No |
ext 32 | 11001001 | 0xc9 | No |
float 32 | 11001010 | 0xca | Yes |
float 64 | 11001011 | 0xcb | Yes |
uint 8 | 11001100 | 0xcc | Yes |
uint 16 | 11001101 | 0xcd | Yes |
uint 32 | 11001110 | 0xce | Yes |
uint 64 | 11001111 | 0xcf | Yes |
int 8 | 11010000 | 0xd0 | Yes |
int 16 | 11010001 | 0xd1 | Yes |
int 32 | 11010010 | 0xd2 | Yes |
int 64 | 11010011 | 0xd3 | Yes |
fixext 1 | 11010100 | 0xd4 | No |
fixext 2 | 11010101 | 0xd5 | No |
fixext 4 | 11010110 | 0xd6 | No |
fixext 8 | 11010111 | 0xd7 | No |
fixext 16 | 11011000 | 0xd8 | No |
str 8 | 11011001 | 0xd9 | Yes |
str 16 | 11011010 | 0xda | Yes |
str 32 | 11011011 | 0xdb | Yes |
array 16 | 11011100 | 0xdc | Yes |
array 32 | 11011101 | 0xdd | Yes |
map 16 | 11011110 | 0xde | Yes |
map 32 | 11011111 | 0xdf | Yes |
negative fixint | 111xxxxx | 0xe0 - 0xff | Yes |
API
pack
Serializes an object into MessagePack. Returns: okio.BufferedSource
Instance version:
MoshiPack().pack(anyObject)
Static version:
MoshiPack.pack(anyObject)
packToByeArray
If you prefer to get a ByteArray
instead of a BufferedSource
you can use this method.
Instance version only
MoshiPack().packToByteArray(anObject)
Static can be done
MoshiPack.pack(anObject).readByteArray()
unpack
Deserializes MessagePack bytes into an Object. Returns: T: Any
Works with ByteArray
and okio.BufferedSource
Instance version:
// T must be valid type so Moshi knows what to deserialize to
val unpacked: T = MoshiPack().unpack(byteArray)
Static version:
val unpacked: T = MoshiPack.upack(byteArray)
Instance version:
val unpacked: T = MoshiPack().unpack(bufferedSource)
Static version:
val unpacked: T = MoshiPack.upack(bufferedSource)
T can be an Object, a List, a Map, and can include generics. Unlike Moshi
you do not need to specify a parameterized type to deserialize to a List with generics. MoshiPack
can infer the paramterized type for you.
The following examples are valid for MoshiPack
:
A typed List
val listCars: List<Car> = MoshiPack.unpack(carMsgPkBytes)
A List of Any
val listCars: List<Any> = MoshiPack.unpack(carMsgPkBytes)
An Object
val car: Car = MoshiPack.unpack(carBytes)
A Map of Any, Any
val car: Map<Any, Any> = MoshiPack.unpack(carBytes)
msgpackToJson
Convert directly from MessagePack bytes to JSON. Use this method for the most effecient implementation as no objects are instantiated in the process. This uses the FormatInterchange
class to match implementations of JsonReader
and a JsonWriter
. If you wanted to say support XML as a direct conversion to and from, you could implement Moshi's JsonReader
and JsonWriter
classes and use the FormatInterchange
class to convert directly to other formats. Returns String
containing a JSON representation of the MessagePack data
Instance versions: (takes ByteArray
or BufferedSource
)
MoshiPack().msgpackToJson(byteArray)
MoshiPack().msgpackToJson(bufferedSource)
Static versions: (takes ByteArray
or BufferedSource
)
MoshiPack.msgpackToJson(byteArray)
MoshiPack.msgpackToJson(bufferedSource)
jsonToMsgpack
Convert directly from JSON to MessagePack bytes. Use this method for the most effecient implementation as no objects are instantiated in the process. Returns BufferedSource
Instance versions: (takes String
or BufferedSource
)
MoshiPack().jsonToMsgpack(jsonString)
MoshiPack().jsonToMsgpack(bufferedSource)
Static versions: (takes String
or BufferedSource
)
MoshiPack.jsonToMsgpack(jsonString)
MoshiPack.jsonToMsgpack(bufferedSource)
MoshiPack - constructor + Moshi builder
The MoshiPack
constructor takes an optional Moshi.Builder.() -> Unit
lambda which is applied to the builder that is used to instantiate the Moshi
instance it uses.
Example adding custom adapter:
val moshiPack = MoshiPack({
add(customAdapter)
})
Moshi
is also a settable property which can be changed on a MoshiPack
instance:
val m = MoshiPack()
m.moshi = Moshi.Builder().build()
The static version of the API also can be passed a lambda to applied to the Moshi.Builder
used to instantiate Moshi
:
MoshiPack.pack(someBytes) { add(customAdapter) }
Forcing integers to write as certain format
- new in v1.0.1
This will force all integers to be packed as the type given. By default the smallest message pack type is used for integers.
val moshiPack = MoshiPack().apply {
writerOptions.writeAllIntsAs = MsgpackIntByte.INT_64
}
Kotiln Support
Since this library is intended for Kotlin use, the moshi-kotlin
artifact is included as a depedency. A KotlinJsonAdapterFactory
is added by default to the instantiated Moshi
that MoshiPack
uses. This adapter allows for the use of Moshi
's annotaions in Kotlin. To learn more about it see the Moshi
documentation.
If you'd like to use Moshi
with out a KotlinJsonAdapterFactory
supply a Moshi
instance for MoshiPack
:
MoshiPack(moshi = Moshi.Builder().build)
ProGuard
From Moshi
's README.md; If you are using ProGuard you might need to add the following options:
-dontwarn okio.**
-dontwarn javax.annotation.**
-keepclasseswithmembers class * {
@com.squareup.moshi.* <methods>;
}
-keep @com.squareup.moshi.JsonQualifier interface *
-keepclassmembers class kotlin.Metadata {
public <methods>;
}
Retrofit
An example of using the retorfit adapter can be found here: https://github.com/davethomas11/MoshiPack_AndroidAppExample