Sending/Receiving Aeson 中的二进制数据
Sending/Receiving binary data in Aeson
似乎 bytestring
不是 aeson
中的可序列化实例,根据 aeson github tracker
下的这些票据: ticket1, ticket2,这可能是明智的做法。
那么,serialize/deserialize二进制对象在aeson
中有什么好的方法呢?这就是 MDN
似乎推荐用于序列化二进制对象的内容:https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data.
更新
正在查看 JSON
source code, I see Word8
is a valid instance. So, would it be best to just send the bytearray as Vector
of Word8
from Javascript (Uint8Array
)?
由于您要求提供一个关于 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
更明确一点,真正的魔法发生在我们为 ByteString
定义的 ToJSON
和 FromJSON
:
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
实例都将按照我们指定的方式自动编码和解码(注意 MyObject
看起来像 "typical" Aeson 定义)。当然,如果您只想对 特定的 ByteString
进行编码,您可以放弃 instance
定义并在 [=18= 的代码中直接进行编码]连载。
ToJSON
只是从输入的 base64 库中调用 encode
,并将生成的 ByteString
(来自 encode
调用)转换为 Text
对象,它是 String
构造函数的输入(一种 Aeson Value
必须从该函数 return 编辑)。
FromJSON
看起来有点可怕,但是——原则上——非常相似。我们采用 String
类型的 Aeson Value
(对于其他我们 return empty
),我们转换 String
中包含的 Text
值反对 ByteString
。然后我们将此 ByteString
提供给 base64 decode
方法,该方法生成 Either
(取决于它是否可以成功解码 ByteString
)。在失败的情况下,我们只是 return 一个空字符串,否则我们将解码后的值提供给对象。
主要功能用作简单的健全性检查。我首先编码 exampleObject
(包含一个 5 字节的二进制字符串)并打印它的值。在下一行中,我们获取该输出并将其命名为 exampleJson
。我们解码 exampleJson
并将其与我们在内存中构建的内存进行比较。正如预期的那样,这些值彼此相等,因此您可以看到编码和解码工作正常。
似乎 bytestring
不是 aeson
中的可序列化实例,根据 aeson github tracker
下的这些票据: ticket1, ticket2,这可能是明智的做法。
那么,serialize/deserialize二进制对象在aeson
中有什么好的方法呢?这就是 MDN
似乎推荐用于序列化二进制对象的内容:https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data.
更新
正在查看 JSON
source code, I see Word8
is a valid instance. So, would it be best to just send the bytearray as Vector
of Word8
from Javascript (Uint8Array
)?
由于您要求提供一个关于 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
更明确一点,真正的魔法发生在我们为 ByteString
定义的 ToJSON
和 FromJSON
:
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
实例都将按照我们指定的方式自动编码和解码(注意 MyObject
看起来像 "typical" Aeson 定义)。当然,如果您只想对 特定的 ByteString
进行编码,您可以放弃 instance
定义并在 [=18= 的代码中直接进行编码]连载。
ToJSON
只是从输入的 base64 库中调用 encode
,并将生成的 ByteString
(来自 encode
调用)转换为 Text
对象,它是 String
构造函数的输入(一种 Aeson Value
必须从该函数 return 编辑)。
FromJSON
看起来有点可怕,但是——原则上——非常相似。我们采用 String
类型的 Aeson Value
(对于其他我们 return empty
),我们转换 String
中包含的 Text
值反对 ByteString
。然后我们将此 ByteString
提供给 base64 decode
方法,该方法生成 Either
(取决于它是否可以成功解码 ByteString
)。在失败的情况下,我们只是 return 一个空字符串,否则我们将解码后的值提供给对象。
主要功能用作简单的健全性检查。我首先编码 exampleObject
(包含一个 5 字节的二进制字符串)并打印它的值。在下一行中,我们获取该输出并将其命名为 exampleJson
。我们解码 exampleJson
并将其与我们在内存中构建的内存进行比较。正如预期的那样,这些值彼此相等,因此您可以看到编码和解码工作正常。