Direct integration with upack for binary MessagePack serialization. MessagePack provides efficient binary serialization that is more compact than JSON while maintaining the same data model.
Writes Scala values directly to upack AST types for binary serialization.
/**
* Write the given Scala value as a MessagePack struct (upack.Msg)
* @param t Value to serialize
* @return upack.Msg representation
*/
def writeMsg[T: Writer](t: T): upack.MsgUsage Examples:
import upickle.default._
import upack._
case class Person(name: String, age: Int)
val person = Person("Alice", 30)
// Convert to upack AST
val msgAst = writeMsg(person)
// Result: upack.Obj("name" -> upack.Str("Alice"), "age" -> upack.Int32(30))
// Convert to binary
val binary = upack.write(msgAst)
// Convert back from binary
val parsed = upack.read(binary)
// Transform to JSON for inspection
val jsonAst = upack.transform(msgAst, ujson.Value)
val jsonString = ujson.write(jsonAst)Built-in support for reading and writing upack.Msg and its subtypes.
/**
* Reader for generic upack.Msg
*/
implicit val MsgValueR: Reader[upack.Msg]
/**
* Writer for generic upack.Msg
*/
implicit val MsgValueW: Writer[upack.Msg]Usage Examples:
import upickle.default._
import upack._
// Create MessagePack data
val msgData = upack.Obj(
"users" -> upack.Arr(
upack.Obj("name" -> upack.Str("Alice"), "age" -> upack.Int32(30)),
upack.Obj("name" -> upack.Str("Bob"), "age" -> upack.Int32(25))
),
"count" -> upack.Int32(2)
)
// Serialize to binary using uPickle
val binary = writeBinary(msgData)
// Deserialize back to upack.Msg
val parsed = readBinary[upack.Msg](binary)
// Access values
val users = parsed.obj("users").arr
val firstUser = users(0).obj
val userName = firstUser("name").str // "Alice"
val userAge = firstUser("age").int32 // 30Direct binary serialization using MessagePack format.
/**
* Reads the given MessagePack input into a Scala value
* @param s MessagePack binary input (Array[Byte], InputStream, etc.)
* @param trace Enable tracing for debugging
* @return Deserialized Scala value of type T
*/
def readBinary[T: Reader](s: upack.Readable, trace: Boolean = false): T
/**
* Write the given Scala value as a MessagePack binary
* @param t Value to serialize
* @param sortKeys Whether to sort object keys alphabetically
* @return MessagePack byte array
*/
def writeBinary[T: Writer](t: T, sortKeys: Boolean = false): Array[Byte]Usage Examples:
import upickle.default._
case class Product(id: Int, name: String, price: Double, tags: List[String])
val product = Product(123, "Laptop", 999.99, List("electronics", "computers"))
// Binary serialization
val binary = writeBinary(product)
println(s"JSON size: ${write(product).getBytes.length} bytes")
println(s"MessagePack size: ${binary.length} bytes")
// Binary deserialization
val parsed = readBinary[Product](binary)
assert(parsed == product)
// Sorted keys for deterministic output
val sortedBinary = writeBinary(product, sortKeys = true)Understanding how Scala types map to MessagePack types.
Usage Examples:
import upickle.default._
import upack._
// Primitive type mappings
val intMsg = writeMsg(42) // upack.Int32(42)
val longMsg = writeMsg(42L) // upack.Int64(42L)
val floatMsg = writeMsg(3.14f) // upack.Float32(3.14f)
val doubleMsg = writeMsg(3.14) // upack.Float64(3.14)
val stringMsg = writeMsg("hello") // upack.Str("hello")
val boolMsg = writeMsg(true) // upack.True
val nullMsg = writeMsg[Option[Int]](None) // upack.Null
// Collection type mappings
val arrayMsg = writeMsg(Array(1, 2, 3)) // upack.Arr(Int32(1), Int32(2), Int32(3))
val listMsg = writeMsg(List("a", "b")) // upack.Arr(Str("a"), Str("b"))
val mapMsg = writeMsg(Map("key" -> "value")) // upack.Obj("key" -> Str("value"))
// Binary data mapping (byte arrays are stored efficiently)
val binaryData = Array[Byte](1, 2, 3, 4, 5)
val binaryMsg = writeMsg(binaryData) // upack.Binary(Array(1, 2, 3, 4, 5))Converting between JSON and MessagePack formats.
Usage Examples:
import upickle.default._
import ujson._
import upack._
case class Data(id: Int, values: List[Double], metadata: Map[String, String])
val data = Data(42, List(1.1, 2.2, 3.3), Map("version" -> "1.0", "type" -> "test"))
// JSON to MessagePack conversion
val jsonString = write(data)
val parsedData = read[Data](jsonString)
val msgPackBinary = writeBinary(parsedData)
// MessagePack to JSON conversion
val fromBinary = readBinary[Data](msgPackBinary)
val backToJson = write(fromBinary)
// Direct AST conversion
val jsonAst = writeJs(data) // ujson.Value
val msgAst = writeMsg(data) // upack.Msg
// Transform between AST types
val jsonFromMsg = upack.transform(msgAst, ujson.Value)
val msgFromJson = ujson.transform(jsonAst, upack.Msg)
// Size comparison
println(s"JSON string: ${jsonString.getBytes("UTF-8").length} bytes")
println(s"MessagePack binary: ${msgPackBinary.length} bytes")Efficient streaming for large MessagePack data.
Usage Examples:
import upickle.default._
import java.io.{FileInputStream, FileOutputStream, ByteArrayOutputStream}
case class LogEntry(timestamp: Long, level: String, message: String)
// Write large dataset to MessagePack file
def writeLogFile(entries: Iterator[LogEntry], filename: String): Unit = {
val output = new FileOutputStream(filename)
try {
entries.foreach { entry =>
val binary = writeBinary(entry)
output.write(binary.length) // Write length prefix
output.write(binary) // Write data
}
} finally {
output.close()
}
}
// Read MessagePack file
def readLogFile(filename: String): Iterator[LogEntry] = {
val input = new FileInputStream(filename)
new Iterator[LogEntry] {
def hasNext: Boolean = input.available() > 0
def next(): LogEntry = {
val length = input.read()
val buffer = new Array[Byte](length)
input.read(buffer)
readBinary[LogEntry](buffer)
}
}
}
// Streaming with upack directly
def streamLargeArray[T: Writer](items: Iterator[T]): Array[Byte] = {
val output = new ByteArrayOutputStream()
val writer = new upack.MsgPackWriter(output)
writer.visitArray(items.size, -1)
items.foreach { item =>
writer.visitValue(transform(item).transform(writer), -1)
}
writer.visitEnd(-1)
output.toByteArray
}Core upack module functions for MessagePack processing and manipulation.
/**
* Read MessagePack input into upack.Msg AST
* @param s MessagePack binary input
* @param trace Enable tracing for debugging
* @return upack.Msg AST representation
*/
def upack.read(s: upack.Readable, trace: Boolean = false): upack.Msg
/**
* Write upack.Msg to MessagePack binary
* @param t upack.Msg to serialize
* @return MessagePack byte array
*/
def upack.write(t: upack.Msg): Array[Byte]
/**
* Write upack.Msg directly to OutputStream
* @param t upack.Msg to serialize
* @param out OutputStream to write to
*/
def upack.writeTo(t: upack.Msg, out: java.io.OutputStream): Unit
/**
* Write upack.Msg to byte array
* @param t upack.Msg to serialize
* @return MessagePack byte array
*/
def upack.writeToByteArray(t: upack.Msg): Array[Byte]Usage Examples:
import upack._
// Create MessagePack AST directly
val msgData = upack.Obj(
"user" -> upack.Str("Alice"),
"score" -> upack.Int32(85),
"active" -> upack.True,
"metadata" -> upack.Obj(
"version" -> upack.Float64(1.2),
"tags" -> upack.Arr(upack.Str("premium"), upack.Str("verified"))
)
)
// Write to binary
val binary = upack.write(msgData)
println(s"MessagePack size: ${binary.length} bytes")
// Read back from binary
val parsed = upack.read(binary)
// Access values
val userName = parsed.obj("user").str // "Alice"
val userScore = parsed.obj("score").int32 // 85
val isActive = parsed.obj("active") == upack.True // true
val version = parsed.obj("metadata").obj("version").float64 // 1.2
// Write to file
val outputStream = new java.io.FileOutputStream("data.msgpack")
try {
upack.writeTo(msgData, outputStream)
} finally {
outputStream.close()
}
// Read from file
val inputStream = new java.io.FileInputStream("data.msgpack")
val fileData = try {
upack.read(inputStream)
} finally {
inputStream.close()
}Validate MessagePack input without parsing to check format correctness.
/**
* Validate MessagePack format without full parsing
* @param s MessagePack input to validate
* @throws upack.UpackException if invalid MessagePack
*/
def upack.validate(s: upack.Readable): UnitUsage Examples:
import upack._
// Validate MessagePack byte arrays
try {
val binaryData = Array[Byte](-108, 4, 85, 115, 101, 114, -91, 65, 108, 105, 99, 101)
upack.validate(binaryData)
println("Valid MessagePack")
} catch {
case e: upack.UpackException => println(s"Invalid MessagePack: ${e.getMessage}")
}
// Validate before expensive parsing
def safeParseMessagePack(input: Array[Byte]): Option[upack.Msg] = {
try {
upack.validate(input)
Some(upack.read(input))
} catch {
case _: upack.UpackException => None
}
}
// Validate large files
def validateMessagePackFile(filename: String): Boolean = {
val inputStream = new java.io.FileInputStream(filename)
try {
upack.validate(inputStream)
true
} catch {
case _: upack.UpackException => false
} finally {
inputStream.close()
}
}Advanced functions for custom MessagePack processing workflows.
/**
* Transform MessagePack using custom visitor
* @param t MessagePack input
* @param v Custom visitor for processing
* @return Processed result
*/
def upack.transform[T](t: upack.Readable, v: upickle.core.Visitor[_, T]): T
/**
* Copy upack.Msg creating a deep clone
* @param t upack.Msg to copy
* @return Deep copy of the input
*/
def upack.copy(t: upack.Msg): upack.MsgUsage Examples:
import upack._
import upickle.core._
// Custom visitor to analyze MessagePack structure
class MessagePackAnalyzer extends Visitor[Any, Map[String, Int]] {
private var typeCounts = Map.empty[String, Int]
private def increment(typeName: String): Unit = {
typeCounts = typeCounts.updated(typeName, typeCounts.getOrElse(typeName, 0) + 1)
}
override def visitArray(length: Int, index: Int) = new ArrVisitor[Any, Map[String, Int]] {
def subVisitor = MessagePackAnalyzer.this
def visitValue(v: Any, index: Int): Unit = increment("array_element")
def visitEnd(index: Int) = { increment("array"); typeCounts }
}
override def visitObject(length: Int, jsonableKeys: Boolean, index: Int) = new ObjVisitor[Any, Map[String, Int]] {
def subVisitor = MessagePackAnalyzer.this
def visitKey(index: Int) = MessagePackAnalyzer.this
def visitKeyValue(s: Any): Unit = increment("object_key")
def visitValue(v: Any, index: Int): Unit = increment("object_value")
def visitEnd(index: Int) = { increment("object"); typeCounts }
}
override def visitString(s: CharSequence, index: Int) = { increment("string"); typeCounts }
override def visitInt32(i: Int, index: Int) = { increment("int32"); typeCounts }
override def visitInt64(i: Long, index: Int) = { increment("int64"); typeCounts }
override def visitFloat64(d: Double, index: Int) = { increment("float64"); typeCounts }
override def visitBool(b: Boolean, index: Int) = { increment("boolean"); typeCounts }
override def visitNull(index: Int) = { increment("null"); typeCounts }
}
// Analyze MessagePack structure
val binaryData = upack.write(upack.Obj(
"numbers" -> upack.Arr(upack.Int32(1), upack.Int64(2L), upack.Float64(3.14)),
"text" -> upack.Str("hello"),
"flag" -> upack.True
))
val analysis = upack.transform(binaryData, new MessagePackAnalyzer())
println("MessagePack structure analysis:")
analysis.foreach { case (typeName, count) =>
println(s" $typeName: $count")
}
// Deep copy MessagePack values
val original = upack.Obj("data" -> upack.Arr(upack.Int32(1), upack.Int32(2)))
val copied = upack.copy(original)
// Modify copy without affecting original
copied.obj("data").arr :+ upack.Int32(3)Working with MessagePack extension types for custom data.
Usage Examples:
import upickle.default._
import upack._
// Custom extension type for timestamps
case class Timestamp(epochMillis: Long)
implicit val timestampRW: ReadWriter[Timestamp] = readwriter[Long].bimap(
(ts: Timestamp) => ts.epochMillis,
(millis: Long) => Timestamp(millis)
)
// Serialize with extension type
val timestamp = Timestamp(System.currentTimeMillis())
val binary = writeBinary(timestamp)
// Custom binary data handling
case class BinaryData(contentType: String, data: Array[Byte])
implicit val binaryDataRW: ReadWriter[BinaryData] = macroRW
val pdfData = BinaryData("application/pdf", loadPdfBytes())
val binaryPdf = writeBinary(pdfData)
val parsedPdf = readBinary[BinaryData](binaryPdf)
// Efficient binary arrays
val imageBytes: Array[Byte] = loadImageBytes()
val msgWithImage = writeMsg(Map(
"filename" -> "photo.jpg",
"size" -> imageBytes.length,
"data" -> imageBytes // Stored efficiently as binary
))
// Working with MessagePack extension types directly
val extData = upack.Ext(42.toByte, Array[Byte](1, 2, 3, 4))
val extBinary = upack.write(extData)
val parsedExt = upack.read(extBinary).asInstanceOf[upack.Ext]
println(s"Extension type: ${parsedExt.tag}, data: ${parsedExt.data.mkString(",")}")/**
* upack.Msg is the base type for all MessagePack AST nodes
*/
sealed trait upack.Msg
/**
* Specific upack message types
*/
case class upack.Obj(value: Map[upack.Msg, upack.Msg]) extends upack.Msg
case class upack.Arr(value: Seq[upack.Msg]) extends upack.Msg
case class upack.Str(value: String) extends upack.Msg
case class upack.Int32(value: Int) extends upack.Msg
case class upack.Int64(value: Long) extends upack.Msg
case class upack.UInt64(value: Long) extends upack.Msg
case class upack.Float32(value: Float) extends upack.Msg
case class upack.Float64(value: Double) extends upack.Msg
case class upack.Binary(value: Array[Byte]) extends upack.Msg
case object upack.True extends upack.Msg
case object upack.False extends upack.Msg
case object upack.Null extends upack.Msg
/**
* upack.Readable represents sources that can provide MessagePack data
*/
trait upack.Readable {
def transform[T](f: upack.Visitor[_, T]): T
}
/**
* Writer for MessagePack binary output
*/
class upack.MsgPackWriter(out: java.io.OutputStream) extends upack.Visitor[Any, Unit]