Bryan O'Sullivan avatar Bryan O'Sullivan committed 1e282f2

Documentation

Comments (0)

Files changed (5)

+-- Module:      Data.Aeson
+-- Copyright:   (c) 2011 MailRank, Inc.
+-- License:     Apache
+-- Maintainer:  Bryan O'Sullivan <bos@mailrank.com>
+-- Stability:   experimental
+-- Portability: portable
+--
+-- Types and functions for working efficiently with JSON data.
+
 module Data.Aeson
     (
-      Array
+    -- * Core JSON types
+      Value(..)
+    , Array
     , Object
-    , Value(..)
+    -- * Type conversion
     , FromJSON(..)
     , ToJSON(..)
+    -- * Constructors and accessors
     , (.=)
     , (.:)
     , object
+    -- * Encoding and parsing
+    , encode
+    , json
     ) where
 
+import Data.Aeson.Encode
+import Data.Aeson.Parser
 import Data.Aeson.Types

Data/Aeson/Encode.hs

 {-# LANGUAGE OverloadedStrings #-}
 
+-- Module:      Data.Aeson.Encode
+-- Copyright:   (c) 2011 MailRank, Inc.
+-- License:     Apache
+-- Maintainer:  Bryan O'Sullivan <bos@mailrank.com>
+-- Stability:   experimental
+-- Portability: portable
+--
+-- Efficiently serialize a JSON value as a lazy 'L.ByteString'.
+
 module Data.Aeson.Encode
     (
-      build
+      fromValue
     , encode
     ) where
 
 import qualified Data.Text as T
 import qualified Data.Vector as V
 
-build :: Value -> Builder
-build Null = fromByteString "null"
-build (Bool b) = fromByteString $ if b then "true" else "false"
-build (Number n) = fromByteString (B.pack (show n))
-build (String s) = string s
-build (Array v)
+-- | Encode a JSON value to a 'Builder'.
+fromValue :: Value -> Builder
+fromValue Null = fromByteString "null"
+fromValue (Bool b) = fromByteString $ if b then "true" else "false"
+fromValue (Number n) = fromByteString (B.pack (show n))
+fromValue (String s) = string s
+fromValue (Array v)
     | V.null v = fromByteString "[]"
     | otherwise = fromChar '[' `mappend`
-                  build (V.unsafeHead v) `mappend`
+                  fromValue (V.unsafeHead v) `mappend`
                   V.foldr f (fromChar ']') (V.unsafeTail v)
-  where f a z = fromChar ',' `mappend` build a `mappend` z
-build (Object m) =
+  where f a z = fromChar ',' `mappend` fromValue a `mappend` z
+fromValue (Object m) =
     case M.toList m of
       (x:xs) -> fromChar '{' `mappend`
                 one x `mappend`
                 foldr f (fromChar '}') xs
       _ -> fromByteString "{}"
   where f a z     = fromChar ',' `mappend` one a `mappend` z
-        one (k,v) = string k `mappend` fromChar ':' `mappend` build v
+        one (k,v) = string k `mappend` fromChar ':' `mappend` fromValue v
 
 string :: T.Text -> Builder
 string s = fromChar '"' `mappend` quote s `mappend` fromChar '"'
         | otherwise  = fromChar c
         where h = showHex (fromEnum c) ""
 
+-- | Efficiently serialize a JSON value as a lazy 'L.ByteString'.
 encode :: ToJSON a => a -> L.ByteString
-encode = toLazyByteString . build . toJSON
+encode = toLazyByteString . fromValue . toJSON
 {-# INLINE encode #-}

Data/Aeson/Parser.hs

 {-# LANGUAGE OverloadedStrings #-}
 
+-- Module:      Data.Aeson.Parser
+-- Copyright:   (c) 2011 MailRank, Inc.
+-- License:     Apache
+-- Maintainer:  Bryan O'Sullivan <bos@mailrank.com>
+-- Stability:   experimental
+-- Portability: portable
+--
+-- Efficiently and correctly parse a JSON string.
+
 module Data.Aeson.Parser
     (
       json
 import Data.Word (Word8)
 import qualified Data.Attoparsec as A
 
+-- | Parse a top-level JSON value.  This must be either an object or
+-- an array.
 json :: Parser Value
 json = do
   c <- skipSpace *> anyChar
   vals <- ((value <* skipSpace) `sepBy` (char8 ',' *> skipSpace)) <* char8 ']'
   return . Array $ Vector.fromList vals
 
+-- | Parse any JSON value.  Use 'json' in preference to this function
+-- if you are parsing data from an untrusted source.
 value :: Parser Value
 value = most <|> (Number <$> double)
  where

Data/Aeson/Types.hs

 {-# LANGUAGE DeriveDataTypeable #-}
 
+-- Module:      Data.Aeson.Types
+-- Copyright:   (c) 2011 MailRank, Inc.
+-- License:     Apache
+-- Maintainer:  Bryan O'Sullivan <bos@mailrank.com>
+-- Stability:   experimental
+-- Portability: portable
+--
+-- Types for working with JSON data.
+
 module Data.Aeson.Types
     (
-      Array
+    -- * Core JSON types
+      Value(..)
+    , Array
     , Object
+    -- * Type conversion
+    , FromJSON(..)
+    , ToJSON(..)
+    -- * Constructors and accessors
     , (.=)
     , (.:)
     , object
-    , Value(..)
-    , FromJSON(..)
-    , ToJSON(..)
     ) where
 
 import Control.Applicative
-import Control.DeepSeq
+import Control.DeepSeq (NFData(..))
 import Data.Map (Map)
 import Data.Text (Text, pack, unpack)
 import Data.Time.Clock (UTCTime)
 type Object = Map Text Value
 type Array = Vector Value
 
+-- | A JSON value represented as a Haskell value.
 data Value = Object Object
            | Array Array
            | String Text
     rnf (Bool b)   = rnf b
     rnf Null       = ()
 
+-- | Construct an 'Object' from a key and a value.
 (.=) :: ToJSON a => Text -> a -> Object
 name .= value = M.singleton name (toJSON value)
 {-# INLINE (.=) #-}
 
+-- | Retrieve the value associated with the given key of an 'Object'.
+-- The result is 'empty' if the key is not present or cannot be
+-- converted to the desired type..
 (.:) :: (Alternative f, FromJSON a) => Object -> Text -> f a
 obj .: key = case M.lookup key obj of
                Nothing -> empty
                Just v  -> fromJSON v
 {-# INLINE (.:) #-}
 
+-- | Create a 'Value' from a list of 'Object's.  If duplicate
+-- keys arise, earlier keys and their associated values win.
 object :: [Object] -> Value
 object = Object . M.unions
 {-# INLINE object #-}
 
+-- | A type that can be converted to JSON.
+--
+-- An example type and instance:
+--
+-- @data Coord { x :: Double, y :: Double }
+--
+-- instance ToJSON Coord where
+--   toJSON (Coord x y) = 'object' [\"x\" '.=' x, \"y\" '.=' y]
+-- @
 class ToJSON a where
     toJSON   :: a -> Value
 
+-- | A type that can be converted from JSON, with the possibility of
+-- failure.
+--
+-- When writing an instance, use 'empty' to make a conversion fail,
+-- e.g. if an 'Object' is missing a required key, or the value is of
+-- the wrong type.
+--
+-- An example type and instance:
+--
+-- @data Coord { x :: Double, y :: Double }
+-- 
+-- instance FromJSON Coord where
+--   fromJSON ('Object' v) = Coord '<$>'
+--                         v '.:' \"x\" '<*>'
+--                         v '.:' \"y\"
+--
+--   \-- A non-'Object' value is of the wrong type, so use 'empty' to fail.
+--   fromJSON _          = 'empty'
+-- @
 class FromJSON a where
     fromJSON :: Alternative f => Value -> f a
 
     fromJSON a      = Just <$> fromJSON a
     {-# INLINE fromJSON #-}
 
+instance (ToJSON a, ToJSON b) => ToJSON (Either a b) where
+    toJSON (Left a)  = toJSON a
+    toJSON (Right b) = toJSON b
+    {-# INLINE toJSON #-}
+    
+instance (FromJSON a, FromJSON b) => FromJSON (Either a b) where
+    fromJSON a = Left <$> fromJSON a <|> Right <$> fromJSON a
+    {-# INLINE fromJSON #-}
+
 instance ToJSON Bool where
     toJSON = Bool
     {-# INLINE toJSON #-}
     fromJSON _          = empty
     {-# INLINE fromJSON #-}
 
+instance ToJSON Integer where
+    toJSON = Number . fromIntegral
+    {-# INLINE toJSON #-}
+
+instance FromJSON Integer where
+    fromJSON (Number n) = pure (floor n)
+    fromJSON _          = empty
+    {-# INLINE fromJSON #-}
+
 instance ToJSON Text where
     toJSON = String
     {-# INLINE toJSON #-}

benchmarks/ParseFile.hs

           let refill = B.hGet h 1024
           result <- parseWith refill json =<< refill
           case result of
-            Done _ r -> rnf r `seq` loop (good+1) bad
+            Done _ r -> loop (good+1) bad
             _        -> loop good (bad+1)
     (good, _) <- loop 0 0
     end <- getCurrentTime
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.