Bryan O'Sullivan avatar Bryan O'Sullivan committed 0ad9b37

Add an accessor for optional object fields.

Comments (0)

Files changed (2)

     -- * Constructors and accessors
     , (.=)
     , (.:)
+    , (.:?)
     , object
     -- * Encoding and parsing
     , encode

Data/Aeson/Types.hs

     -- * Constructors and accessors
     , (.=)
     , (.:)
+    , (.:?)
     , object
     ) where
 
 import Control.Applicative
 import Control.DeepSeq (NFData(..))
 import Data.Map (Map)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as LB
+import Data.Text.Encoding (decodeUtf8, encodeUtf8)
 import Data.Text (Text, pack, unpack)
+import qualified Data.Text.Lazy as LT
 import Data.Time.Clock (UTCTime)
 import Data.Time.Format (formatTime, parseTime)
 import Data.Typeable (Typeable)
 {-# 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..
+-- The result is 'empty' if the key is not present or the value cannot
+-- be converted to the desired type.
+--
+-- This accessor is appropriate if the key and value /must/ be present
+-- in an object for it to be valid.  If the key and value are
+-- optional, use '(.:?)' instead.
 (.:) :: (Alternative f, FromJSON a) => Object -> Text -> f a
 obj .: key = case M.lookup key obj of
                Nothing -> empty
                Just v  -> fromJSON v
 {-# INLINE (.:) #-}
 
+-- | Retrieve the value associated with the given key of an 'Object'.
+-- The result is 'Nothing' if the key is not present, or 'empty' if
+-- the value cannot be converted to the desired type.
+--
+-- This accessor is most useful if the key and value can be absent
+-- from an object without affecting its validity.  If the key and
+-- value are mandatory, use '(.:?)' instead.
+(.:?) :: (Alternative f, FromJSON a) => Object -> Text -> f (Maybe a)
+obj .:? key = case M.lookup key obj of
+               Nothing -> pure Nothing
+               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
     fromJSON _          = empty
     {-# INLINE fromJSON #-}
 
+instance ToJSON LT.Text where
+    toJSON = String . LT.toStrict
+    {-# INLINE toJSON #-}
+
+instance FromJSON LT.Text where
+    fromJSON (String t) = pure (LT.fromStrict t)
+    fromJSON _          = empty
+    {-# INLINE fromJSON #-}
+
+instance ToJSON B.ByteString where
+    toJSON = String . decodeUtf8
+    {-# INLINE toJSON #-}
+
+instance FromJSON B.ByteString where
+    fromJSON (String t) = pure . encodeUtf8 $ t
+    fromJSON _          = empty
+    {-# INLINE fromJSON #-}
+
+instance ToJSON LB.ByteString where
+    toJSON = toJSON . B.concat . LB.toChunks
+    {-# INLINE toJSON #-}
+
+instance FromJSON LB.ByteString where
+    fromJSON (String t) = pure . LB.fromChunks . (:[]) . encodeUtf8 $ t
+    fromJSON _          = empty
+    {-# INLINE fromJSON #-}
+
 mapA :: (Applicative f) => (t -> f a) -> [t] -> f [a]
 mapA f = go
   where
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.