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 定义的 ToJSONFromJSON:

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 并将其与我们在内存中构建的内存进行比较。正如预期的那样,这些值彼此相等,因此您可以看到编码和解码工作正常。