packages feed

fields-json 0.1 → 0.2

raw patch · 7 files changed

+350/−125 lines, 7 filesdep +base64-bytestringdep +utf8-stringPVP ok

version bump matches the API change (PVP)

Dependencies added: base64-bytestring, utf8-string

API changes (from Hackage documentation)

- Text.JSON.Fields: class JSField a
- Text.JSON.Fields: field :: JSField a => String -> a -> JSFields
- Text.JSON.Fields: instance [incoherent] JSField JSFields
- Text.JSON.Fields: instance [incoherent] JSField [JSFields]
- Text.JSON.Fields: instance [incoherent] ToJSON a => JSField (IO a)
- Text.JSON.Fields: instance [incoherent] ToJSON a => JSField a
- Text.JSON.Fields: json :: JSFields -> IO JSValue
- Text.JSON.ToJSON: class ToJSON a
- Text.JSON.ToJSON: instance [incoherent] ToJSON Bool
- Text.JSON.ToJSON: instance [incoherent] ToJSON Int
- Text.JSON.ToJSON: instance [incoherent] ToJSON Integer
- Text.JSON.ToJSON: instance [incoherent] ToJSON JSValue
- Text.JSON.ToJSON: instance [incoherent] ToJSON String
- Text.JSON.ToJSON: instance [incoherent] ToJSON a => ToJSON (Map String a)
- Text.JSON.ToJSON: instance [incoherent] ToJSON a => ToJSON [a]
- Text.JSON.ToJSON: toJSON :: ToJSON a => a -> JSValue
+ Text.JSON.FromJSValue: class FromJSValue a where fromJSValue j = runIdentity $ withJSValue j $ liftM fromJSValueM askJSValue fromJSValueM = liftM fromJSValue askJSValue
+ Text.JSON.FromJSValue: fromJSValue :: FromJSValue a => JSValue -> Maybe a
+ Text.JSON.FromJSValue: fromJSValueCustomList :: (JSValueContainer c, MonadReader c m) => [m (Maybe a)] -> m (Maybe [a])
+ Text.JSON.FromJSValue: fromJSValueCustomMany :: (JSValueContainer c, MonadReader c m) => m (Maybe a) -> m (Maybe [a])
+ Text.JSON.FromJSValue: fromJSValueField :: (JSValueContainer c, MonadReader c m, FromJSValue a) => String -> m (Maybe a)
+ Text.JSON.FromJSValue: fromJSValueFieldBase64 :: (JSValueContainer c, MonadReader c m) => String -> m (Maybe ByteString)
+ Text.JSON.FromJSValue: fromJSValueFieldCustom :: (JSValueContainer c, MonadReader c m) => String -> m (Maybe a) -> m (Maybe a)
+ Text.JSON.FromJSValue: fromJSValueM :: (FromJSValue a, JSValueContainer c, MonadReader c m) => m (Maybe a)
+ Text.JSON.FromJSValue: instance [incoherent] FromJSValue Bool
+ Text.JSON.FromJSValue: instance [incoherent] FromJSValue ByteString
+ Text.JSON.FromJSValue: instance [incoherent] FromJSValue Int
+ Text.JSON.FromJSValue: instance [incoherent] FromJSValue Integer
+ Text.JSON.FromJSValue: instance [incoherent] FromJSValue JSValue
+ Text.JSON.FromJSValue: instance [incoherent] FromJSValue String
+ Text.JSON.FromJSValue: instance [incoherent] FromJSValue a => FromJSValue (Maybe a)
+ Text.JSON.FromJSValue: instance [incoherent] FromJSValue a => FromJSValue [a]
+ Text.JSON.FromJSValue: withJSValue :: Monad m => JSValue -> ReaderT JSValue m a -> m a
+ Text.JSON.Gen: data JSONGenT m a
+ Text.JSON.Gen: instance [incoherent] (Monad m, Functor m) => Applicative (JSONGenT m)
+ Text.JSON.Gen: instance [incoherent] Functor m => Functor (JSONGenT m)
+ Text.JSON.Gen: instance [incoherent] Monad m => Monad (JSONGenT m)
+ Text.JSON.Gen: instance [incoherent] Monad m => MonadReader (Seq (String, JSValue)) (JSONGenT m)
+ Text.JSON.Gen: instance [incoherent] MonadIO m => MonadIO (JSONGenT m)
+ Text.JSON.Gen: instance [incoherent] MonadTrans JSONGenT
+ Text.JSON.Gen: object :: Monad m => String -> JSONGenT m () -> JSONGenT m ()
+ Text.JSON.Gen: objects :: Monad m => String -> [JSONGenT m ()] -> JSONGenT m ()
+ Text.JSON.Gen: runJSONGen :: JSONGen () -> JSValue
+ Text.JSON.Gen: runJSONGenT :: Monad m => JSONGenT m () -> m JSValue
+ Text.JSON.Gen: type JSONGen = JSONGenT Identity
+ Text.JSON.Gen: value :: (Monad m, ToJSValue a) => String -> a -> JSONGenT m ()
+ Text.JSON.Gen: valueM :: (Monad m, ToJSValue a) => String -> m a -> JSONGenT m ()
+ Text.JSON.JSValueContainer: class JSValueContainer a
+ Text.JSON.JSValueContainer: getJSValue :: JSValueContainer a => a -> JSValue
+ Text.JSON.JSValueContainer: instance [incoherent] JSValueContainer (Seq (String, JSValue))
+ Text.JSON.JSValueContainer: instance [incoherent] JSValueContainer JSValue
+ Text.JSON.JSValueContainer: setJSValue :: JSValueContainer a => JSValue -> a -> a
+ Text.JSON.ToJSValue: class ToJSValue a
+ Text.JSON.ToJSValue: instance [incoherent] Real a => ToJSValue a
+ Text.JSON.ToJSValue: instance [incoherent] ToJSValue Bool
+ Text.JSON.ToJSValue: instance [incoherent] ToJSValue JSValue
+ Text.JSON.ToJSValue: instance [incoherent] ToJSValue String
+ Text.JSON.ToJSValue: instance [incoherent] ToJSValue a => ToJSValue (Map String a)
+ Text.JSON.ToJSValue: instance [incoherent] ToJSValue a => ToJSValue (Maybe a)
+ Text.JSON.ToJSValue: instance [incoherent] ToJSValue a => ToJSValue [a]
+ Text.JSON.ToJSValue: toJSValue :: ToJSValue a => a -> JSValue

Files

fields-json.cabal view
@@ -1,10 +1,10 @@ Name:                fields-json-Version:             0.1+Version:             0.2 Synopsis: Abusing monadic syntax JSON objects generation.              Description:    Generation of big, complex JSON objects with Text.JSON is painful. And autoderivation is not always posible.-  Check haddock of Fields module for more info.+  Check documentation of Text.JSON.Gen for more info.  License:             BSD3 License-file:        LICENSE@@ -13,22 +13,30 @@ Stability:           Development Category:            Web Build-type:          Simple-Cabal-version:       >=1.2+Cabal-version:       >=1.4   library   exposed-modules: -     Text.JSON.Fields-     Text.JSON.ToJSON+     Text.JSON.Gen+     Text.JSON.ToJSValue+     Text.JSON.FromJSValue+     Text.JSON.JSValueContainer    hs-source-dirs: src -+  GHC-Options: -Werror   build-depends: base >= 4 && < 5   build-depends: json >= 0.4.4   build-depends: containers+  build-depends: base64-bytestring >= 0.1.0.2+  build-depends: utf8-string  >= 0.3.6   build-depends: mtl   extensions:    FlexibleInstances,                  TypeSynonymInstances,                  UndecidableInstances,                  OverlappingInstances,-                 IncoherentInstances+                 IncoherentInstances,+                 DeriveFunctor,+                 GeneralizedNewtypeDeriving,+                 MultiParamTypeClasses,+                 StandaloneDeriving
− src/Text/JSON/Fields.hs
@@ -1,76 +0,0 @@--------------------------------------------------------------------------------- |--- Module      :  Text.JSON.Fields--- Copyright   :  (c) Scrive 2011--- License     :  BSD-style (see the LICENSE file in the distribution)------ Maintainer  :  mariusz@scrive.com--- Stability   :  development--- Portability :  portable------ Abusing monadic do notation library for generating JSON object. --- Hard-binded to json package from hackage.--- Main ideas------ * Overloaded function 'field', that may set values of fields of different types - 'Bool', 'Int', 'String', lists  etc.------ * Internal IO - value of the field can be IO a, is we know how to put a into JSON. That means that there is no need to do prebinding ------ * Compositionality - value of field can also be fields. Easy to do embeded  objects------ * Monadic notation - it really looks nicer then composition with '.' or some magic combinator------ >--- > json $ do--- >     field "a" "a"--- >     field "b" [1,2,3]--- >     field "c" $ do--- >         field "x" True--- >         field "y" False--- >--- --- Will generate json object ---  {a : "a", b: [1,2,3], c: {x: true, y : false}} -----module Text.JSON.Fields (json, JSField(..))where--import Text.JSON-import Text.JSON.ToJSON-import Data.Map as Map-import Control.Monad.State.Strict---type JSFields = State ([(String, IO JSValue)]) ()---{- | Function for changing 'JSFields' into real JSON object -}--json :: JSFields -> IO JSValue-json fields = fmap (JSObject . toJSObject) $ sequence $ fmap pack $ execState fields []--{- | The 'JSField' class instances are object that in some sence can be interpreted as value of JSON object fields. -     To derive new instances use existing instances since internal structure 'JSFields' is hidden.--}--class JSField a where-  field :: String -> a -> JSFields-  -instance (ToJSON a) => JSField a where  -  field n v = modify $ \s -> (n, return $ toJSON v) : s--instance (ToJSON a) => JSField (IO a) where  -  field n v = modify $ \s -> (n, fmap toJSON v) : s--instance JSField (JSFields) where  -  field n v = modify $ \s -> (n, fmap toJSON val) : s-   where-      val = fmap Map.fromList (sequence $ fmap pack $ execState v [])-  -instance JSField [JSFields] where  -  field n fs = modify $ \s -> (n, fmap toJSON $ mapM vals fs) : s-      where-        vals f = fmap Map.fromList  (sequence $ fmap pack $ execState f [])--pack :: (Functor f) => (a, f b) -> f (a, b)-pack (name, comp)=  fmap (\res -> (name,res)) comp
+ src/Text/JSON/FromJSValue.hs view
@@ -0,0 +1,137 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Text.JSON.ToJSON+-- Copyright   :  (c) Scrive 2011+-- License     :  BSD-style (see the LICENSE file in the distribution)+--+-- Maintainer  :  mariusz@scrive.com+-- Stability   :  development+-- Portability :  portable+--+-- Interface for extracting data from JSValue.+--+++module Text.JSON.FromJSValue (+                               -- * Basic Parsing+                                 FromJSValue(..)+                               -- * Data Extraction+                               , fromJSValueField+                               , fromJSValueFieldBase64+                               , fromJSValueFieldCustom+                               , fromJSValueCustomMany+                               , fromJSValueCustomList+                               -- * Running+                               , withJSValue  ) where+import Text.JSON+import Text.JSON.JSValueContainer+import Control.Monad+import Control.Monad.Reader+import qualified Data.ByteString.UTF8 as BS+import qualified Data.ByteString.Base64 as BASE64+import Data.Ratio+import Control.Monad.Identity+import Data.Maybe+import Text.JSON.String+++-- | Structures that can be 'parsed' from JSON. Instances must declare either 'fromJSValue' (parse directly from 'JSValue') or 'fromJSValueM' (uses 'MonadReader')+class FromJSValue a where+    fromJSValue :: JSValue -> Maybe a+    fromJSValue j = runIdentity $ withJSValue j $ liftM fromJSValueM askJSValue+    fromJSValueM :: (JSValueContainer c , MonadReader c m) => m (Maybe a)+    fromJSValueM = liftM fromJSValue askJSValue++-- ---------------------------------------------------------------------------+-- Instances for basic datatypes+--++instance FromJSValue JSValue where+    fromJSValue = Just++instance FromJSValue String where+    fromJSValue (JSString string) = Just $ fromJSString string+    fromJSValue _ = Nothing++instance FromJSValue BS.ByteString where+    fromJSValue s = fmap BS.fromString (fromJSValue s)++instance FromJSValue Integer where+    fromJSValue (JSRational _ r) = Just $ numerator r+    fromJSValue _ = Nothing++instance FromJSValue Int where+    fromJSValue j = liftM fromIntegral (fromJSValue j :: Maybe Integer)++instance FromJSValue Bool where+    fromJSValue (JSBool v) = Just $ v+    fromJSValue _ = Nothing++instance (FromJSValue a) => FromJSValue [a] where+    fromJSValue (JSArray list) = let plist = map fromJSValue list+                                 in if (all isJust plist)+                                     then Just $ map fromJust plist+                                     else Nothing++    fromJSValue _ = Nothing++instance (FromJSValue a) => FromJSValue (Maybe a) where+    fromJSValue = join . fromJSValue+++-- ----------------------------------------------------------------++-- | Getting JSON part of envirement+askJSValue :: (JSValueContainer c , MonadReader c m) => m JSValue+askJSValue = liftM getJSValue ask+++-- | Reading the value that is on some field. With field if current JSON is not object+fromJSValueField ::  (JSValueContainer c , MonadReader c m, FromJSValue a) => String -> m (Maybe a)+fromJSValueField s = liftM fromObject askJSValue+    where+      fromObject (JSObject object) = join (fmap fromJSValue (lookup s $ fromJSObject object))+      fromObject _ = Nothing++-- | Version of 'fromJSValueField' for Base64 encoded data to  avoid memory leak.+fromJSValueFieldBase64 ::(JSValueContainer c , MonadReader c m) =>String -> m  (Maybe BS.ByteString)+fromJSValueFieldBase64 s =  liftM dc (fromJSValueField s)+    where dc s' = case (fmap BASE64.decode s') of+                            Just (Right r) -> Just r+                            _ -> Nothing++-- | Generalization of 'fromJSValueField'. Does not use 'FromJSValue' instances+fromJSValueFieldCustom :: (JSValueContainer c , MonadReader c m) =>  String -> m (Maybe a) -> m (Maybe a)+fromJSValueFieldCustom s digger = do+    mobj <- fromJSValueField s+    case mobj of+         Just obj -> local (setJSValue obj) (digger)+         Nothing -> return Nothing+         +-- | Runs parser on each element of underlaying json. Returns Just iff JSON is array.+fromJSValueCustomMany :: (JSValueContainer c , MonadReader c m) => m (Maybe a) -> m (Maybe [a])+fromJSValueCustomMany digger = fromJSValueCustomList (repeat digger)++-- | Generalization of 'fromJSValueCustomMany', where each element of array can have different parser.+fromJSValueCustomList :: (JSValueContainer c , MonadReader c m) => [m (Maybe a)] -> m (Maybe [a])+fromJSValueCustomList diggers = do+    mlist <- fromJSValueM+    case mlist of+         Nothing -> return Nothing+         Just list ->  runDiggers list diggers+    where+         runDiggers (j:js) (d:ds) = do+             mres <- local (setJSValue j) d+             case mres of+                 Just res -> do+                     mress <- runDiggers js ds+                     case mress of+                         Just ress -> return $ Just (res:ress)+                         _ -> return Nothing+                 _ -> return Nothing+         runDiggers _ _ = return $ Just []+         +-- ----------------------------------------------------------------+-- | Simple runner+withJSValue :: (Monad m) => JSValue -> ReaderT JSValue m a -> m a+withJSValue j a = runReaderT a j
+ src/Text/JSON/Gen.hs view
@@ -0,0 +1,121 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Text.JSON.Gen+-- Copyright   :  (c) Scrive 2011+-- License     :  BSD-style (see the LICENSE file in the distribution)+--+-- Maintainer  :  andrzej@scrive.com+-- Stability   :  development+-- Portability :  portable+--+-- Abusing monadic 'do' notation library for generating JSON object. +-- Hard-binded to json package from hackage.+-- Main ideas+--+-- * Overloaded function 'value' to set values in underlying JSON - 'Bool', 'Int', 'String', lists  etc.+--+-- * JSON generation may not be pure  with 'valueM'. You can perform some IO while generating JSON. This is usefull skip useless inner binding.+--+-- * Compositionality - use 'object' to easy create JSON objects+--+-- * Monadic notation - it really looks nicer then composition with '.' or some magic combinator+--+-- >+-- > runJSONGen $ do+-- >     value "a" "a"+-- >     value "b" [1,2,3]+-- >     object "c" $ do+-- >         value "x" True+-- >         value "y" False+-- >+-- +-- Will generate json object +--  {a : "a", b: [1,2,3], c: {x: true, y : false}} +++ module Text.JSON.Gen (+    module Text.JSON.ToJSValue+    -- * Basic types+  , JSONGen+  , JSONGenT+    -- * Runners+  , runJSONGen+  , runJSONGenT+    -- * Creating JSON's+  , value+  , valueM+  , object+  , objects+  ) where++import Control.Applicative+import Control.Monad+import Control.Monad.Identity+import Control.Monad.Trans+import Control.Monad.State.Strict+import Control.Monad.Reader+import Control.Monad.Reader.Class++import Data.Foldable+import Data.Sequence as S+import Text.JSON+import Text.JSON.ToJSValue+import Text.JSON.JSValueContainer++-- --------------------------------------------------------------++-- | Basic types+type JSONGen = JSONGenT Identity++newtype JSONGenT m a = JSONGenT (StateT (Seq (String, JSValue)) m a)+  deriving (Applicative, Functor, Monad, MonadTrans)+++-- | This instance gives us the ability to use FromJSValue function while generating.+instance (Monad m) => MonadReader (Seq (String, JSValue)) (JSONGenT m) where+    ask = JSONGenT (get)+    local f (JSONGenT m) = JSONGenT $ do+                             s <- get+                             put (f s)+                             res <- m+                             put s+                             return res+++instance MonadIO m => MonadIO (JSONGenT m) where+  liftIO = JSONGenT . liftIO++-- --------------------------------------------------------------++-- | Simple runner+runJSONGen :: JSONGen () -> JSValue+runJSONGen = runIdentity . runJSONGenT+++runJSONGenT :: Monad m => JSONGenT m () -> m JSValue+runJSONGenT (JSONGenT f) = getJSValue `liftM` execStateT f S.empty++-- --------------------------------------------------------------++-- | Set pure value under given name in final JSON object+value :: (Monad m, ToJSValue a) => String -> a -> JSONGenT m ()+value name val = JSONGenT $ modify (|> (name, toJSValue val))++-- | Monadic verion of 'value'+valueM :: (Monad m, ToJSValue a) => String -> m a -> JSONGenT m ()+valueM name mval = lift mval >>= value name+++-- | Embed other JSON object as field in resulting JSON object.+object :: Monad m => String -> JSONGenT m () -> JSONGenT m ()+object name json = JSONGenT $ do+  val <- lift $ runJSONGenT json+  modify (|> (name, toJSValue val))+++-- | Version for lists of objects.  +objects :: Monad m => String -> [JSONGenT m ()] -> JSONGenT m ()+objects name jsons = JSONGenT $ do+  val <- mapM (lift . runJSONGenT) jsons+  modify (|> (name, toJSValue val))+
+ src/Text/JSON/JSValueContainer.hs view
@@ -0,0 +1,35 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Text.JSON.ToJSON+-- Copyright   :  (c) Scrive 2011+-- License     :  BSD-style (see the LICENSE file in the distribution)+--+-- Maintainer  :  mariusz@scrive.com+-- Stability   :  development+-- Portability :  portable+--+-- Data structures that hold JSValue inside.+-- Value can be extracted or replaced, but is always inside.+--+++module Text.JSON.JSValueContainer (JSValueContainer(..))where++import Text.JSON+import Data.Sequence as S+import Data.Foldable+import Text.JSON++class JSValueContainer a where+    getJSValue :: a -> JSValue+    setJSValue :: JSValue -> a -> a++instance JSValueContainer JSValue where+    getJSValue = id+    setJSValue= const++-- Instance used by generator. Note that putting is not ~ workeround.+instance JSValueContainer (Seq (String, JSValue)) where+    getJSValue s = JSObject $ toJSObject $ toList s+    setJSValue (JSObject o) _ = S.fromList $ fromJSObject o+    setJSValue _ s = s -- We could throw error, but why should we bothered
− src/Text/JSON/ToJSON.hs
@@ -1,42 +0,0 @@--------------------------------------------------------------------------------- |--- Module      :  Text.JSON.ToJSON--- Copyright   :  (c) Scrive 2011--- License     :  BSD-style (see the LICENSE file in the distribution)------ Maintainer  :  mariusz@scrive.com--- Stability   :  development--- Portability :  portable------ Unifing some structures to JSValue------module Text.JSON.ToJSON (ToJSON(..))where--import Text.JSON-import Data.Map--class ToJSON a where-  toJSON:: a -> JSValue--instance ToJSON JSValue where-  toJSON = id-  -instance ToJSON String where-  toJSON = JSString . toJSString --instance ToJSON Bool where-  toJSON = JSBool--instance ToJSON Int where-  toJSON = JSRational True . toRational-  -instance ToJSON Integer where-  toJSON = JSRational True . toRational-  -instance (ToJSON a) =>  ToJSON [a] where-  toJSON = JSArray . fmap toJSON -  -instance (ToJSON a) => ToJSON (Map String a) where  -  toJSON = JSObject . toJSObject . toList . (fmap toJSON) 
+ src/Text/JSON/ToJSValue.hs view
@@ -0,0 +1,42 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Text.JSON.ToJSON+-- Copyright   :  (c) Scrive 2011+-- License     :  BSD-style (see the LICENSE file in the distribution)+--+-- Maintainer  :  mariusz@scrive.com+-- Stability   :  development+-- Portability :  portable+--+-- Unifing some structures so they can be serialized to JSValue+--+++module Text.JSON.ToJSValue (ToJSValue(..))where++import Text.JSON+import Data.Map as M++class ToJSValue a where+  toJSValue :: a -> JSValue++instance ToJSValue JSValue where+  toJSValue = id++instance ToJSValue Bool where+  toJSValue = JSBool++instance ToJSValue String where+  toJSValue = JSString . toJSString++instance Real a => ToJSValue a where+  toJSValue = JSRational True . toRational+  +instance ToJSValue a => ToJSValue [a] where+  toJSValue = JSArray . fmap toJSValue++instance ToJSValue a => ToJSValue (M.Map String a) where+  toJSValue = JSObject . toJSObject . M.toList . fmap toJSValue++instance ToJSValue a => ToJSValue (Maybe a) where+  toJSValue = maybe JSNull toJSValue