Copyright | (c) Dong Han 2019 |
---|---|
License | BSD |
Maintainer | winterland1989@gmail.com |
Stability | experimental |
Portability | non-portable |
Safe Haskell | None |
Language | Haskell2010 |
This module provides an interface similar to Z.Data.JSON, to work with MessagePack binary format.
Maybe a
convert toNil
inNothing
case, anda
inJust
case.- Use
Int64
(signed) orWord64
(unsigned) type to marshall int type format, smaller types will sliently truncate when overflow. - Use
Double
to marshall float type format,Float
may lost precision. - Use
Scientific
to marshallExt
0x00/0x01
type. - Use
SystemTime
to marshallExt
0xFF
type. - Record's field label are preserved.
We use MessagePack extension type -1 to encode/decode
SystemTime
andUTCTime
:0xc7 12 -1 nanoseconds in 32-bit unsigned int seconds in 64-bit signed int We deliberately use ext type 0x00(positive) and 0x01(negative) to represent large numbers(
Integer
,Scientific
,Fixed
,DiffTime
...):0xc7 XXXXXXXX 0x00 base10 exponent(MessagePack int format) coefficient(big endian 256-base limbs Use a MessagePack implementation supporting ext type to marshall it, result value is coefficient * (10 ^ exponent).
The easiest way to use the library is to define target data type, deriving Generic
and MessagePack
instances, e.g.
import GHC.Generics (Generic) import qualified Z.Data.MessagePack as MessagePack import qualified Z.Data.Text as T data Person = Person {name :: T.Text, age :: Int} deriving (Show, Generic) deriving anyclass (MessagePack.MessagePack) > MessagePack.encode Person{ name="Alice", age=16 } > [130,164,110,97,109,101,165,65,108,105,99,101,163,97,103,101,16]
MessagePack is a schemaless format, which means the encoded data can be recovered into some form(Value
in haskell case)
without providing data definition, e.g. the data encoded above:
[130, 164, 110, 97, 109, 101, 165, 65, 108, 105, 99, 101, 163, 97, 103, 101, 16] 0x82 0xA4 'n' 'a' 'm' 'e' 0xA5 'A' 'l' 'i' 'c' 'e' 0xA3 'a' 'g' 'e' int map str str str 16 2kvs 4bytes 5bytes 3bytes
This property makes it suitable for passing data across language boundary, e.g. from a static typed language to a dynamic one, at the cost of a lower space efficiency(i.e. type tag and field label).
Synopsis
- class MessagePack a where
- data Value
- defaultSettings :: Settings
- data Settings = Settings {}
- snakeCase :: String -> Text
- trainCase :: String -> Text
- readMessagePackFile :: (HasCallStack, MessagePack a) => CBytes -> IO a
- writeMessagePackFile :: (HasCallStack, MessagePack a) => CBytes -> a -> IO ()
- decode :: MessagePack a => Bytes -> (Bytes, Either DecodeError a)
- decode' :: MessagePack a => Bytes -> Either DecodeError a
- decodeChunks :: (MessagePack a, Monad m) => m Bytes -> Bytes -> m (Bytes, Either DecodeError a)
- encode :: MessagePack a => a -> Bytes
- encodeChunks :: MessagePack a => a -> [Bytes]
- type DecodeError = Either ParseError ConvertError
- type ParseError = [Text]
- parseValue :: Bytes -> (Bytes, Either ParseError Value)
- parseValue' :: Bytes -> Either ParseError Value
- parseValueChunks :: Monad m => m Bytes -> Bytes -> m (Bytes, Either ParseError Value)
- parseValueChunks' :: Monad m => m Bytes -> Bytes -> m (Either ParseError Value)
- gToValue :: GToValue f => Settings -> f a -> Value
- gFromValue :: GFromValue f => Settings -> Value -> Converter (f a)
- gEncodeMessagePack :: GEncodeMessagePack f => Settings -> f a -> Builder ()
- convertValue :: MessagePack a => Value -> Either ConvertError a
- newtype Converter a = Converter {
- runConverter :: forall r. ([PathElement] -> Text -> r) -> (a -> r) -> r
- fail' :: Text -> Converter a
- (<?>) :: Converter a -> PathElement -> Converter a
- prependContext :: Text -> Converter a -> Converter a
- data PathElement
- data ConvertError = ConvertError {
- errPath :: [PathElement]
- errMsg :: Text
- typeMismatch :: Text -> Text -> Value -> Converter a
- fromNil :: Text -> a -> Value -> Converter a
- withBool :: Text -> (Bool -> Converter a) -> Value -> Converter a
- withStr :: Text -> (Text -> Converter a) -> Value -> Converter a
- withBin :: Text -> (Bytes -> Converter a) -> Value -> Converter a
- withArray :: Text -> (Vector Value -> Converter a) -> Value -> Converter a
- withKeyValues :: Text -> (Vector (Value, Value) -> Converter a) -> Value -> Converter a
- withFlatMap :: Text -> (FlatMap Value Value -> Converter a) -> Value -> Converter a
- withFlatMapR :: Text -> (FlatMap Value Value -> Converter a) -> Value -> Converter a
- (.:) :: MessagePack a => FlatMap Value Value -> Text -> Converter a
- (.:?) :: MessagePack a => FlatMap Value Value -> Text -> Converter (Maybe a)
- (.:!) :: MessagePack a => FlatMap Value Value -> Text -> Converter (Maybe a)
- convertField :: (Value -> Converter a) -> FlatMap Value Value -> Text -> Converter a
- convertFieldMaybe :: (Value -> Converter a) -> FlatMap Value Value -> Text -> Converter (Maybe a)
- convertFieldMaybe' :: (Value -> Converter a) -> FlatMap Value Value -> Text -> Converter (Maybe a)
- (.=) :: MessagePack v => Text -> v -> (Value, Value)
- object :: [(Value, Value)] -> Value
- (.!) :: MessagePack v => Text -> v -> KVItem
- object' :: KVItem -> Builder ()
- data KVItem
MessagePack Class
class MessagePack a where Source #
Type class for encode & decode MessagePack.
Nothing
fromValue :: Value -> Converter a Source #
toValue :: a -> Value Source #
encodeMessagePack :: a -> Builder () Source #
Instances
Representation of MessagePack data.
Bool !Bool | true or false |
Int !Int64 | an integer |
Float !Float | a floating point number |
Double !Double | a floating point number |
Str !Text | a UTF-8 string |
Bin !Bytes | a byte array |
Array !(Vector Value) | a sequence of objects |
Map !(Vector (Value, Value)) | key-value pairs of objects |
Ext | |
Nil | nil |
Instances
defaultSettings :: Settings Source #
Settings T.pack T.pack False
Generic encode/decode Settings
Snake casing a pascal cased constructor name or camel cased field name, words are always lower cased and separated by an underscore.
Train casing a pascal cased constructor name or camel cased field name, words are always lower cased and separated by a hyphen.
Encode & Decode
readMessagePackFile :: (HasCallStack, MessagePack a) => CBytes -> IO a Source #
Decode a MessagePack
instance from file.
writeMessagePackFile :: (HasCallStack, MessagePack a) => CBytes -> a -> IO () Source #
Encode a MessagePack
instance to file.
decode :: MessagePack a => Bytes -> (Bytes, Either DecodeError a) Source #
Decode a MessagePack bytes, return any trailing bytes.
decode' :: MessagePack a => Bytes -> Either DecodeError a Source #
Decode a MessagePack doc, trailing bytes are not allowed.
decodeChunks :: (MessagePack a, Monad m) => m Bytes -> Bytes -> m (Bytes, Either DecodeError a) Source #
Decode MessagePack doc chunks, return trailing bytes.
encode :: MessagePack a => a -> Bytes Source #
Directly encode data to MessagePack bytes.
encodeChunks :: MessagePack a => a -> [Bytes] Source #
Encode data to MessagePack bytes chunks.
type DecodeError = Either ParseError ConvertError Source #
type ParseError = [Text] #
Type alias for error message
parse into MessagePack Value
parseValue :: Bytes -> (Bytes, Either ParseError Value) Source #
Parse Value
without consuming trailing bytes.
parseValue' :: Bytes -> Either ParseError Value Source #
Parse Value
, if there're bytes left, parsing will fail.
parseValueChunks :: Monad m => m Bytes -> Bytes -> m (Bytes, Either ParseError Value) Source #
Increamental parse Value
without consuming trailing bytes.
parseValueChunks' :: Monad m => m Bytes -> Bytes -> m (Either ParseError Value) Source #
Increamental parse Value
, if there're bytes left, parsing will fail.
Generic FromValue, ToValue & EncodeMessagePack
gEncodeMessagePack :: GEncodeMessagePack f => Settings -> f a -> Builder () Source #
Convert Value
to Haskell data
convertValue :: MessagePack a => Value -> Either ConvertError a Source #
Run a Converter
with input value.
Converter
provides a monadic interface to convert protocol IR (e.g.Value
) to Haskell ADT.
Converter | |
|
Instances
(<?>) :: Converter a -> PathElement -> Converter a infixl 9 #
Add (JSON) Path context to a converter
When converting a complex structure, it helps to annotate (sub)converters with context, so that if an error occurs, you can find its location.
withFlatMapR "Person" $ \o -> Person <$> o .: "name" <?> Key "name" <*> o .: "age" <?> Key "age"
(Standard methods like (.:)
already do this.)
With such annotations, if an error occurs, you will get a (JSON) Path location of that error.
prependContext :: Text -> Converter a -> Converter a #
Add context to a failure message, indicating the name of the structure being converted.
prependContext "MyType" (fail "[error message]") -- Error: "converting MyType failed, [error message]"
data PathElement #
Elements of a (JSON) Value path used to describe the location of an error.
Key !Text | Path element of a key into an object, "object.key". |
Index !Int | Path element of an index into an array, "array[index]". |
Embedded | path of a embedded (JSON) String |
Instances
data ConvertError #
Error info with (JSON) Path info.
ConvertError | |
|
Instances
:: Text | The name of the type you are trying to convert. |
-> Text | The MessagePack value type you expecting to meet. |
-> Value | The actual value encountered. |
-> Converter a |
Produce an error message like converting XXX failed, expected XXX, encountered XXX
.
withKeyValues :: Text -> (Vector (Value, Value) -> Converter a) -> Value -> Converter a Source #
Directly use Map
as key-values for further converting.
withFlatMap :: Text -> (FlatMap Value Value -> Converter a) -> Value -> Converter a Source #
Take a Map
as an 'FM.FlatMap Value Value', on key duplication prefer first one.
withFlatMapR :: Text -> (FlatMap Value Value -> Converter a) -> Value -> Converter a Source #
Take a Map
as an 'FM.FlatMap Value Value', on key duplication prefer last one.
(.:) :: MessagePack a => FlatMap Value Value -> Text -> Converter a Source #
Retrieve the value associated with the given key of an Map
.
The result is empty
if the key is not present or the value cannot
be converted to the desired type.
This accessor is appropriate if the key and value must be present
in an object for it to be valid. If the key and value are
optional, use .:?
instead.
(.:?) :: MessagePack a => FlatMap Value Value -> Text -> Converter (Maybe a) Source #
Retrieve the value associated with the given key of an Map
. The
result is Nothing
if the key is not present or if its value is Nil
,
or fail if the value cannot be converted to the desired type.
This accessor is most useful if the key and value can be absent
from an object without affecting its validity. If the key and
value are mandatory, use .:
instead.
convertFieldMaybe :: (Value -> Converter a) -> FlatMap Value Value -> Text -> Converter (Maybe a) Source #
Variant of .:?
with explicit converter function.
convertFieldMaybe' :: (Value -> Converter a) -> FlatMap Value Value -> Text -> Converter (Maybe a) Source #
Variant of .:!
with explicit converter function.
Helper for manually writing instance.
(.=) :: MessagePack v => Text -> v -> (Value, Value) infixr 8 Source #
Connect key and value to a tuple to be used with object
.
A newtype for Builder
, whose semigroup's instance is to connect kv builder and sum kv length.