Haskell Aeson JSON кодирование bytestrings - PullRequest
0 голосов
/ 03 декабря 2018

Мне нужно сериализовать запись в Haskell, и я пытаюсь сделать это с Aeson .Проблема в том, что некоторые поля являются ByteStrings, и я не могу понять из примеров, как их кодировать.Моя идея состоит в том, чтобы сначала преобразовать их в текст через base64.Вот что у меня есть (я поставил «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"

Было бы хорошо, если бы не было необходимости вручную определять новые экземпляры ToJSON иFromJSON для каждой записи, потому что у меня есть довольно много разных записей с байтовыми строками в них.

1 Ответ

0 голосов
/ 03 декабря 2018

parseJson необходимо вернуть значение типа Parser B.ByteString, поэтому вам просто нужно вызвать pure для возвращаемого значения B64.decode.

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

Здесь я выбралвозвращать mzero оба, если вы пытаетесь декодировать что-либо, кроме строки JSON, и если есть проблема с декодированием base-64.

Аналогично, toJSON нужно просто кодировать Textзначение, которое вы создаете из закодированной в Base64 ByteString.

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

Возможно, вы захотите использовать оболочку нового типа вместо определения экземпляров ToJSON и FromJSON для B.ByteString напрямую.

...