yesod-json 0.2.3 → 0.3.1
raw patch · 2 files changed
+99/−54 lines, 2 filesdep +attoparsec-conduitdep +bytestringdep +conduitdep −attoparsec-enumeratordep −unordered-containersdep ~aesondep ~containersdep ~shakespeare-js
Dependencies added: attoparsec-conduit, bytestring, conduit, safe, wai, wai-extra, yesod-routes
Dependencies removed: attoparsec-enumerator, unordered-containers
Dependency ranges changed: aeson, containers, shakespeare-js, text, yesod-core
Files
- Yesod/Json.hs +85/−45
- yesod-json.cabal +14/−9
Yesod/Json.hs view
@@ -1,88 +1,128 @@-{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE TypeSynonymInstances, OverloadedStrings #-} {-# LANGUAGE CPP #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Yesod.Json ( -- * Convert from a JSON value defaultLayoutJson , jsonToRepJson+ -- * Convert to a JSON value , parseJsonBody- -- * Compatibility wrapper for old API- , Json- , jsonScalar- , jsonList- , jsonMap+ , parseJsonBody_++ -- * Produce JSON values+ , J.Value (..)+ , object+ , array+ , (.=)++ -- * Convenience functions+ , jsonOrRedirect ) where -import Yesod.Handler (GHandler)+import Yesod.Handler (GHandler, waiRequest, lift, invalidArgs, redirect) import Yesod.Content ( ToContent (toContent), RepHtmlJson (RepHtmlJson), RepHtml (RepHtml) , RepJson (RepJson), Content (ContentBuilder) ) import Yesod.Core (defaultLayout, Yesod) import Yesod.Widget (GWidget)+import Yesod.Routes.Class+import Control.Arrow (second)+import Control.Applicative ((<$>))+import Control.Monad (join) import qualified Data.Aeson as J+import Data.Aeson ((.=)) import qualified Data.Aeson.Encode as JE import Data.Aeson.Encode (fromValue)-import Data.Attoparsec.Enumerator (iterParser)-import Data.Text (pack)-import Control.Arrow (first)-import Control.Monad.Trans.Class (lift)-#if MIN_VERSION_aeson(0, 4, 0)-import Data.HashMap.Strict (fromList)-#else-import Data.Map (fromList)-#endif+import Data.Conduit.Attoparsec (sinkParser)+import Data.Text (Text, pack) import qualified Data.Vector as V import Text.Julius (ToJavascript (..)) import Data.Text.Lazy.Builder (fromLazyText) import Data.Text.Lazy.Encoding (decodeUtf8)-#if MIN_VERSION_aeson(0, 5, 0) import Data.Text.Lazy.Builder (toLazyText) import qualified Blaze.ByteString.Builder.Char.Utf8 as Blaze-#endif+import Data.Conduit (($$))+import Network.Wai (requestBody, requestHeaders)+import Network.Wai.Parse (parseHttpAccept)+import qualified Data.ByteString.Char8 as B8+import Safe (headMay) instance ToContent J.Value where-#if MIN_VERSION_aeson(0, 5, 0) toContent = flip ContentBuilder Nothing . Blaze.fromLazyText . toLazyText . fromValue-#else- toContent = flip ContentBuilder Nothing . fromValue-#endif --- | Provide both an HTML and JSON representation for a piece of data, using--- the default layout for the HTML output ('defaultLayout').-defaultLayoutJson :: Yesod master- => GWidget sub master ()- -> J.Value+-- | Provide both an HTML and JSON representation for a piece of+-- data, using the default layout for the HTML output+-- ('defaultLayout').+--+-- /Since: 0.3.0/+defaultLayoutJson :: (Yesod master, J.ToJSON a)+ => GWidget sub master () -- ^ HTML+ -> a -- ^ JSON -> GHandler sub master RepHtmlJson defaultLayoutJson w json = do RepHtml html' <- defaultLayout w- return $ RepHtmlJson html' $ toContent json---- | Wraps the 'Content' generated by 'jsonToContent' in a 'RepJson'.-jsonToRepJson :: J.Value -> GHandler sub master RepJson-jsonToRepJson = return . RepJson . toContent+ return $ RepHtmlJson html' $ toContent (J.toJSON json) --- | Parse the request body as a JSON value.+-- | Wraps a data type in a 'RepJson'. The data type must+-- support conversion to JSON via 'J.ToJSON'. ----- /Since: 0.2.3/-parseJsonBody :: GHandler sub master J.Value-parseJsonBody = lift $ iterParser J.json'+-- /Since: 0.3.0/+jsonToRepJson :: J.ToJSON a => a -> GHandler sub master RepJson+jsonToRepJson = return . RepJson . toContent . J.toJSON +-- | Parse the request body to a data type as a JSON value. The+-- data type must support conversion from JSON via 'J.FromJSON'.+-- If you want the raw JSON value, just ask for a @'J.Result'+-- 'J.Value'@.+--+-- /Since: 0.3.0/+parseJsonBody :: J.FromJSON a => GHandler sub master (J.Result a)+parseJsonBody = do+ req <- waiRequest+ fmap J.fromJSON $ lift $ requestBody req $$ sinkParser J.json' -type Json = J.Value+-- | Same as 'parseJsonBody', but return an invalid args response on a parse+-- error.+parseJsonBody_ :: J.FromJSON a => GHandler sub master a+parseJsonBody_ = do+ ra <- parseJsonBody+ case ra of+ J.Error s -> invalidArgs [pack s]+ J.Success a -> return a -jsonScalar :: String -> Json-jsonScalar = J.String . pack+instance ToJavascript J.Value where+ toJavascript = fromLazyText . decodeUtf8 . JE.encode -jsonList :: [Json] -> Json-jsonList = J.Array . V.fromList+-- | Convert a list of pairs to an 'J.Object'.+object :: J.ToJSON a => [(Text, a)] -> J.Value+object = J.object . map (second J.toJSON) -jsonMap :: [(String, Json)] -> Json-jsonMap = J.Object . fromList . map (first pack)+-- | Convert a list of values to an 'J.Array'.+array :: J.ToJSON a => [a] -> J.Value+array = J.Array . V.fromList . map J.toJSON -instance ToJavascript J.Value where- toJavascript = fromLazyText . decodeUtf8 . JE.encode+-- | jsonOrRedirect simplifies the scenario where a POST handler sends a different+-- response based on Accept headers:+--+-- 1. 200 with JSON data if the client prefers application/json (e.g. AJAX).+--+-- 2. 3xx otherwise, following the PRG pattern.+jsonOrRedirect :: (Yesod master, J.ToJSON a)+ => Route master -- ^ Redirect target+ -> a -- ^ Data to send via JSON+ -> GHandler sub master RepJson+jsonOrRedirect r j = do+ q <- acceptsJson+ if q then jsonToRepJson (J.toJSON j)+ else redirect r+ where+ acceptsJson = maybe False ((== "application/json") . B8.takeWhile (/= ';'))+ . join+ . fmap (headMay . parseHttpAccept)+ . lookup "Accept" . requestHeaders+ <$> waiRequest
yesod-json.cabal view
@@ -1,5 +1,5 @@ name: yesod-json-version: 0.2.3+version: 0.3.1 license: BSD3 license-file: LICENSE author: Michael Snoyman <michael@snoyman.com>@@ -14,19 +14,24 @@ library build-depends: base >= 4 && < 5- , yesod-core >= 0.9 && < 0.10- , aeson >= 0.3- , text >= 0.8 && < 0.12- , shakespeare-js >= 0.10 && < 0.11+ , yesod-core >= 0.10.1 && < 0.11+ , yesod-routes < 0.1+ , aeson >= 0.5+ , text >= 0.8 && < 1.0+ , shakespeare-js >= 0.11 && < 0.12 , vector >= 0.9- , containers >= 0.2 && < 0.5- , unordered-containers+ , containers >= 0.2 , blaze-builder- , attoparsec-enumerator >= 0.3 && < 0.4+ , attoparsec-conduit >= 0.2 && < 0.3+ , conduit >= 0.2 && < 0.3 , transformers >= 0.2.2 && < 0.3+ , wai >= 1.1 && < 1.2+ , wai-extra >= 1.1 && < 1.2+ , bytestring >= 0.9 && < 0.10+ , safe >= 0.2 && < 0.4 exposed-modules: Yesod.Json ghc-options: -Wall source-repository head type: git- location: git://github.com/yesodweb/yesod.git+ location: https://github.com/yesodweb/yesod