packages feed

hydra-0.8.0: src/main/haskell/Hydra/Ext/Json/Serde.hs

module Hydra.Ext.Json.Serde where

import Hydra.Core
import Hydra.Compute
import Hydra.Graph
import Hydra.Ext.Json.Coder
import Hydra.Tools.Bytestrings
import qualified Hydra.Json as Json

import qualified Data.ByteString.Lazy as BS
import qualified Control.Monad as CM
import qualified Data.Aeson as A
import qualified Data.Aeson.KeyMap as AKM
import qualified Data.Aeson.Key as AK
import qualified Data.List as L
import qualified Data.Map as M
import qualified Data.Text as T
import qualified Data.Vector as V
import qualified Data.Scientific as SC
import qualified Data.Char as C
import qualified Data.String as String


aesonValueToBytes :: A.Value -> BS.ByteString
aesonValueToBytes = A.encode

aesonValueToJsonValue :: A.Value -> Json.Value
aesonValueToJsonValue v = case v of
  A.Object km -> Json.ValueObject $ M.fromList (mapPair <$> AKM.toList km)
    where
      mapPair (k, v) = (AK.toString k, aesonValueToJsonValue v)
  A.Array a -> Json.ValueArray (aesonValueToJsonValue <$> V.toList a)
  A.String t -> Json.ValueString $ T.unpack t
  A.Number s -> Json.ValueNumber $ SC.toRealFloat s
  A.Bool b -> Json.ValueBoolean b
  A.Null -> Json.ValueNull

bytesToAesonValue :: BS.ByteString -> Either String A.Value
bytesToAesonValue = A.eitherDecode

bytesToJsonValue :: BS.ByteString -> Either String Json.Value
bytesToJsonValue bs = aesonValueToJsonValue <$> bytesToAesonValue bs

jsonByteStringCoder :: Type -> Flow Graph (Coder Graph Graph Term BS.ByteString)
jsonByteStringCoder typ = do
  coder <- jsonCoder typ
  return Coder {
    coderEncode = fmap jsonValueToBytes . coderEncode coder,
    coderDecode = \bs -> case bytesToJsonValue bs of
        Left msg -> fail $ "JSON parsing failed: " ++ msg
        Right v -> coderDecode coder v}

-- | A convenience which maps typed terms to and from pretty-printed JSON strings, as opposed to JSON objects
jsonStringCoder :: Type -> Flow Graph (Coder Graph Graph Term String)
jsonStringCoder typ = do
  serde <- jsonByteStringCoder typ
  return Coder {
    coderEncode = fmap bytesToString . coderEncode serde,
    coderDecode = coderDecode serde . stringToBytes}

jsonValueToAesonValue :: Json.Value -> A.Value
jsonValueToAesonValue v = case v of
    Json.ValueArray l -> A.Array $ V.fromList (jsonValueToAesonValue <$> l)
    Json.ValueBoolean b -> A.Bool b
    Json.ValueNull -> A.Null
    Json.ValueNumber d -> A.Number $ SC.fromFloatDigits d
    Json.ValueObject m -> A.Object $ AKM.fromList (mapPair <$> M.toList m)
      where
        mapPair (k, v) = (AK.fromString k, jsonValueToAesonValue v)
    Json.ValueString s -> A.String $ T.pack s

jsonValueToBytes :: Json.Value -> BS.ByteString
jsonValueToBytes = aesonValueToBytes . jsonValueToAesonValue

jsonValueToString :: Json.Value -> String
jsonValueToString = bytesToString . jsonValueToBytes

stringToJsonValue :: String -> Either String Json.Value
stringToJsonValue = bytesToJsonValue . stringToBytes