简体   繁体   English

Haskell Aeson json编码字节串

[英]Haskell Aeson json encoding bytestrings

I need to serialize a record in Haskell, and am trying to do it with Aeson . 我需要序列化Haskell中的记录,并尝试与Aeson一起使用 The problem is that some of the fields are ByteStrings, and I can't work out from the examples how to encode them. 问题在于某些字段是ByteStrings,我无法从示例中了解如何对它们进行编码。 My idea is to first convert them to text via base64. 我的想法是首先通过base64将它们转换为文本。 Here is what I have so far (I put 'undefined' where I didn't know what to do): 到目前为止,这是我所拥有的(我将“ undefined”放在我不知道该怎么做的地方):

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where

import qualified Data.Aeson as J
import qualified Data.ByteString as B
import qualified Data.ByteString.Base64 as B64
import qualified Data.Text as T
import qualified Data.Text.Encoding as E
import qualified GHC.Generics as G

data Data = Data
  { number :: Int
  , bytestring :: B.ByteString
  } deriving (G.Generic, Show)

instance J.ToJSON Data where
  toEncoding = J.genericToEncoding J.defaultOptions

instance J.FromJSON Data

instance J.FromJSON B.ByteString where
  parseJSON = undefined

instance J.ToJSON B.ByteString where
  toJSON = undefined

byteStringToText :: B.ByteString -> T.Text
byteStringToText = E.decodeUtf8 . B64.encode

textToByteString :: T.Text -> B.ByteString
textToByteString txt =
  case B64.decode . E.encodeUtf8 $ txt of
    Left err -> error err
    Right bs -> bs

encodeDecode :: Data -> Maybe Data
encodeDecode = J.decode . J.encode

main :: IO ()
main = print $ encodeDecode $ Data 1 "A bytestring"

It would be good if it was not necessary to manually define new instances of ToJSON and FromJSON for every record, because I have quite a few different records with bytestrings in them. 如果不需要为每条记录手动定义ToJSON和FromJSON的新实例,那将是很好的选择,因为我有很多不同的记录,其中包含字节串。

parseJson needs to return a value of type Parser B.ByteString , so you just need to call pure on the return value of B64.decode . parseJson需要返回Parser B.ByteString类型的值,因此您只需要在B64.decode的返回值上调用pure

import Control.Monad

-- Generalized to any MonadPlus instance, not just Either String
textToByteString :: MonadPlus m =>  T.Text -> m B.ByteString
textToByteString = case B64.decode (E.encodeUtf8 x) of
                     Left _ -> mzero
                     Right bs -> pure bs

instance J.FromJSON B.ByteString where
  parseJSON (J.String x) = textToByteString x
  parseJSON _ = mzero

Here, I've chosen to return mzero both if you try to decode anything other than a JSON string and if there is a problem with the base-64 decoding. 在这里,如果您尝试解码除JSON字符串以外的任何内容,以及base-64解码存在问题,我都选择返回mzero

Likewise, toJSON needs just needs to encode the Text value you create from the base64-encoded ByteString. 同样, toJSON只需要对从base64编码的ByteString创建的Text值进行编码。

instance J.ToJSON B.ByteString where
  toJSON = J.toJSON . byteStringToText

You might want to consider using a newtype wrapper instead of defining the ToJSON and FromJSON instances on B.ByteString directly. 您可能要考虑使用新型包装器,而不是直接在B.ByteString上定义ToJSONFromJSON实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM