在Aeson中发送/接收二进制数据

时间:2016-05-05 15:50:45

标签: haskell aeson

似乎bytestring不是aeson中的可序列化实例,根据aeson github tracker ticket1ticket2下的这些门票,这可能是明智之举。

那么,在aeson中序列化/反序列化二进制对象的好方法是什么呢?这是MDN似乎建议用于序列化二进制对象的内容:https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data

更新

查看JSON source code,我看到Word8是一个有效的实例。那么,最好是从Javascript(Uint8Array)发送Vector Word8的bytearray吗?

1 个答案:

答案 0 :(得分:2)

由于你要求一个关于base64编码的例子,你的数据要通过JSON发送,我已经提出了一个粗略的例子:

{-# LANGUAGE OverloadedStrings #-}
module Main(main) where
import qualified Control.Applicative as App
import qualified Data.Aeson as A
import Data.Aeson.Types
import Data.ByteString
import qualified Data.ByteString.Lazy as LB
import Data.ByteString.Base64
import Data.Maybe (fromMaybe)
import Data.Text
import Data.Text.Encoding

data MyObject = MyObject { objectName :: Text, objectData :: ByteString } deriving (Eq)

instance FromJSON ByteString where
  parseJSON (String t) = pure $ (either (const "") id . decode . encodeUtf8) t
  parseJSON _ = App.empty

instance ToJSON ByteString where
  toJSON = String . decodeUtf8 . encode

instance FromJSON MyObject where
  parseJSON (Object v) = MyObject <$> v .: "name" <*> v .: "data"
  parseJSON _ = App.empty

instance ToJSON MyObject where
  toJSON obj = object [ "name" .= objectName obj, "data" .= objectData obj ]

exampleObject :: MyObject
exampleObject = MyObject "example" "\x01\x02\x03\x04\x05"

exampleJson :: LB.ByteString
exampleJson = "{\"data\":\"AQIDBAU=\",\"name\":\"example\"}"

main :: IO ()
main = do
  print $ A.encode exampleObject
  print $ exampleObject == fromMaybe (MyObject "fail" "") (A.decode exampleJson)

哪个应产生以下输出:

"{\"data\":\"AQIDBAU=\",\"name\":\"example\"}"
True

为了更明确一点,我们对ToJSON的{​​{1}}和FromJSON的定义发生了真正的魔力:

ByteString

简而言之,这为我们希望它如何序列化(严格)instance FromJSON ByteString where parseJSON (String t) = pure $ (either (const "") id . decode . encodeUtf8) t parseJSON _ = App.empty instance ToJSON ByteString where toJSON = String . decodeUtf8 . encode 类型的任何实例提供了Aeson方向。现在遇到的任何ByteString实例都将按照我们的指定自动编码和解码(请注意ByteString看起来像“典型的”Aeson定义。当然,如果您只想编码特定 MyObject,您可以放弃ByteString定义,并在instance序列化代码中执行此操作。

MyObject只需调用输入中base64库中的ToJSON,并将生成的encode(来自ByteString调用)转换为encode对象,它是Text构造函数的输入(必须从此函数返回的一种Aeson String)。

Value看起来有点可怕,但 - 原则上 - 非常相似。我们接受FromJSON类型的Aeson Value(对于我们返回的任何其他内容String),我们将empty对象中包含的Text值转换为{ {1}}。然后,我们将此String提供给base64 ByteString方法,该方法生成ByteString(取决于它是否可以成功解码decode)。我们只是在失败的情况下返回一个空字符串,否则我们将解码后的值提供给对象。

主要功能用作简单的健全性检查。我首先编码Either(包含一个5字节的二进制字符串)并打印其值。在下一行中,我们将该输出称为ByteString。我们解码exampleObject并将其与我们在内存中构造的内存进行比较。这些值 - 如预期的那样 - 彼此相等,因此您可以看到编码和解码正常工作。