{-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE IncoherentInstances #-} {-# LANGUAGE DeriveDataTypeable #-} {-| 1. /Pretty prints/ Re-export module Data.JSON2.Pretty. Example of use: @ *ghci> pp $ mkObj [(show x, x) | x <- [0..7]] {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7} @ 2. /Renders JSON to String/ Haskell value has a JSON string: @ HASKELL value JSON string (toString . toJson) ------------------------------- ----------------------------- Just \"bla\" :: Maybe String \"bla\" Nothing :: Maybe String null Left 1 :: Either Int Int [[1], []] Right 1 :: Either Int Int [[], [1]] \'a\' :: Char 97 () :: () [] (1, \"bla\") :: (Int, String) [1, \"bla\"] fromList [1,2,3,4] :: Set Int [1, 2, 3, 4] fromList [(\"0\",0),(\"1\",10),(\"2\",20)] {\"0\": 0, \"1\": 10, \"2\": 20} :: Map String Int @ 3. /Conversion haskell values from and to JSON/ This module provides many instances classes `FromJson` and `ToJson` for haskell data types. See instances class `ToJson` for SQL (HDBC) in module Database.HDBC.JSON2 (package json2-hdbc). /Adding Instance class ToJson or FromJson/ Transformation of algebraic product in `Json`. For example: @ data Person = Person {name :: String, age:: Int} deriving (Typeable, Show, Eq) @ @ instance ToJson Person where toJson (Person s n) = toJson [toJson s, toJson n] @ @ instance FromJson Person where safeFromJson (JArray [js, jn]) = return $ Person (fromJson js) (fromJson jn) safeFromJson x = mkError x @ Converting `Bounded` and `Enum` values to Json. For example: @ data Color = Red | Green | Blue | Black deriving (Typeable, Show, Eq, Enum, Bounded) @ @ instance ToJson Color where toJson = JNumber . toRational . fromEnum instance FromJson Color where safeFromJson (JNumber x) = checkBoundsEnum (toEnum . round) x safeFromJson x = mkError x @ -} module Data.JSON2 ( -- * Re-export module for pretty printing module Data.JSON2.Pretty -- * Base data types , Json (..) , Jsons (..) -- * Renders JSON to string , toString -- * Conversion haskell values from and to JSON , ToJson (toJson) , FromJson(safeFromJson) , fromJson -- * Building JSON objects , emptyObj, (.=), mkObj -- * Merges JSON objects , (+=), merges, mergeRec ) where import Data.JSON2.Internal import Data.JSON2.Types import Data.JSON2.Pretty import Data.Typeable (Typeable) import Data.List import Data.Ratio import Data.Int import Data.Word import Data.Map (Map) import qualified Data.Map as Map import Data.Set (Set) import qualified Data.Set as Set import qualified Data.ByteString as B (ByteString) import qualified Data.ByteString.Lazy as L (ByteString) import qualified Data.ByteString.UTF8 as U8 (fromString, toString) import qualified Data.ByteString.Lazy.UTF8 as LU8 (fromString, toString) infixl 4 += infixr 6 .= -------------- Conversion from and to JSON -------------------------------------- -- | Class for conversion from `Json`. class Typeable a => ToJson a where toJson :: a -> Json -- | Class for conversion from `Json`. class Typeable a => FromJson a where safeFromJson :: Json -> ConvResult a -- | Conversion from `Json`. fromJson :: FromJson a => Json -> a fromJson x = case safeFromJson x of Left e -> error (show e) Right r -> r ---------------- Building and manipulation JSON objects --------- -- Building JSON objects -- | Create empty `Json` object. -- -- > pp $ emptyObj == {} emptyObj :: Json emptyObj = JObject Map.empty -- | Create single `Json` object. -- -- > pp ("key" .= (Just False)) == {"key": false} (.=) :: (ToJson v, Typeable v) => String -> v -> Json k .= v = JObject $ Map.singleton k (toJson v) -- | Create `Json` object from list. -- -- > pp $ mkObj [("a", "old"), ("a", "new"), ("bb", "other")] == {"a": "new", "bb": "other"} mkObj :: (ToJson v, Typeable v) => [(String ,v)] -> Json mkObj xs = JObject $ Map.fromList (map (\(k,v) -> (k, toJson v)) xs) -- * Merges JSON objects -- | Merge two `JObject`. Other `Json` values interpreted as `emptyObj`. -- -- > pp $ ("a" .= "old") += ("a" .= "new") += ("bb" .= "other") == {"a": "new", "bb": "other"} -- -- > obj += emptyObj == emptyObj += obj -- > obj += obj == emptyObj += obj == obj += emptyObj -- > obj1 += (obj2 += obj3) == (obj1 += obj2) += obj3 (+=) :: Json -> Json -> Json (+=) (JObject x ) (JObject y) = JObject $ Map.union y x (+=) (JObject x ) _ = JObject x (+=) _ (JObject y) = JObject y (+=) _ _ = emptyObj -- | Merge `Json` objects from list. -- -- > pp $ merges [("a" .= "old"), ("a" .= "new"), ("bb" .= "other")] == {"a": "new", "bb": "other"} merges :: [Json] -> Json merges = foldl (+=) emptyObj -- | Recursively merge the two `Json` objects. mergeRec :: Json -> Json -> Json mergeRec ox@(JObject x) oy@(JObject y) = mergeRec' ox oy mergeRec _ _ = emptyObj mergeRec' (JObject x) (JObject y) = JObject $ Map.unionWith mergeRec' x y mergeRec' _ js = js ------------------------- Instances ----------------------------- -- Mix instances instance ToJson Json where toJson = id instance FromJson Json where safeFromJson = return instance ToJson () where toJson () = JArray [] instance FromJson () where safeFromJson (JArray []) = return () safeFromJson x = mkError x instance ToJson a => ToJson (Maybe a) where toJson = maybe JNull toJson instance FromJson a => FromJson (Maybe a) where safeFromJson x = if x == JNull then return Nothing else return $ Just (fromJson x) instance ToJson Bool where toJson = JBool instance FromJson Bool where safeFromJson (JBool x) = return x safeFromJson x = mkError x instance (ToJson a, ToJson b) => ToJson (Either a b) where toJson (Right x) = toJson [[], [x]] toJson (Left x) = toJson [[x], []] instance (FromJson a, FromJson b) => FromJson (Either a b) where safeFromJson (JArray [JArray [], JArray [x]]) = (return . Right . fromJson) x safeFromJson (JArray [JArray [x], JArray []]) = (return . Left . fromJson) x safeFromJson x = mkError x -- Instances String instance ToJson String where toJson = JString instance FromJson String where safeFromJson (JString x) = return x safeFromJson x = mkError x instance ToJson B.ByteString where toJson = JString . U8.toString instance FromJson B.ByteString where safeFromJson (JString x) = (return . U8.fromString) x safeFromJson x = mkError x instance ToJson L.ByteString where toJson = JString . LU8.toString instance FromJson L.ByteString where safeFromJson (JString x) = (return . LU8.fromString) x safeFromJson x = mkError x -- Instances Char instance ToJson Char where toJson = jsonifyIntegral . fromEnum instance FromJson Char where safeFromJson (JNumber x) = checkBoundsEnum (toEnum . round) x safeFromJson x = mkError x -- Instances Numeric instance ToJson Integer where toJson = jsonifyIntegral instance FromJson Integer where safeFromJson (JNumber x) = return (round x) safeFromJson x = mkError x -- Instances Int instance ToJson Int where toJson = jsonifyIntegral instance FromJson Int where safeFromJson (JNumber x) = checkBoundsIntegral round x safeFromJson x = mkError x instance ToJson Int8 where toJson = jsonifyIntegral instance FromJson Int8 where safeFromJson (JNumber x) = checkBoundsIntegral round x safeFromJson x = mkError x instance ToJson Int16 where toJson = jsonifyIntegral instance FromJson Int16 where safeFromJson (JNumber x) = checkBoundsIntegral round x safeFromJson x = mkError x instance ToJson Int32 where toJson = jsonifyIntegral instance FromJson Int32 where safeFromJson (JNumber x) = checkBoundsIntegral round x safeFromJson x = mkError x instance ToJson Int64 where toJson = jsonifyIntegral instance FromJson Int64 where safeFromJson (JNumber x) = checkBoundsIntegral round x safeFromJson x = mkError x -- Instances Word instance ToJson Word where toJson = jsonifyIntegral instance FromJson Word where safeFromJson (JNumber x) = checkBoundsIntegral round x safeFromJson x = mkError x instance ToJson Word8 where toJson = jsonifyIntegral instance FromJson Word8 where safeFromJson (JNumber x) = checkBoundsIntegral round x safeFromJson x = mkError x instance ToJson Word16 where toJson = jsonifyIntegral instance FromJson Word16 where safeFromJson (JNumber x) = checkBoundsIntegral round x safeFromJson x = mkError x instance ToJson Word32 where toJson = jsonifyIntegral instance FromJson Word32 where safeFromJson (JNumber x) = checkBoundsIntegral round x safeFromJson x = mkError x instance ToJson Word64 where toJson = jsonifyIntegral instance FromJson Word64 where safeFromJson (JNumber x) = checkBoundsIntegral round x safeFromJson x = mkError x -- jsonifyIntegral :: (Integral a) => a -> Json jsonifyIntegral i = JNumber (fromIntegral i % 1) -- Instances Floating and Rational instance ToJson Double where toJson = jsonifyRealFrac instance FromJson Double where safeFromJson (JNumber x) = checkInfinite fromRational x safeFromJson x = mkError x instance ToJson Float where toJson = jsonifyRealFrac instance FromJson Float where safeFromJson (JNumber x) = checkInfinite fromRational x safeFromJson x = mkError x instance ToJson Rational where toJson = JNumber instance FromJson Rational where safeFromJson (JNumber x) = return x safeFromJson x = mkError x -- jsonifyRealFrac :: (RealFrac a) => a -> Json jsonifyRealFrac i = JNumber (approxRational i 1e-666) -- ** Instances Containers instance ToJson a => ToJson [a] where toJson = JArray . map toJson instance FromJson a => FromJson [a] where safeFromJson (JArray xs) = return (map fromJson xs) safeFromJson x = mkError x instance ToJson v => ToJson (Map String v) where toJson = JObject . Map.map toJson instance FromJson v => FromJson (Map String v) where safeFromJson (JObject m) = return $ Map.map fromJson m safeFromJson x = mkError x instance ToJson a => ToJson (Set a) where toJson = JArray . (map toJson) . Set.toList instance (FromJson a, Ord a) => FromJson (Set a) where safeFromJson (JArray xs) = return . Set.fromList $ map fromJson xs safeFromJson x = mkError x -- Instances Tuples instance ( ToJson t1, ToJson t2) => ToJson (t1, t2) where toJson (x1, x2) = JArray [toJson x1, toJson x2] instance (FromJson t1, FromJson t2) => FromJson (t1, t2) where safeFromJson (JArray [x1, x2]) = return (fromJson x1, fromJson x2) safeFromJson x = mkError x instance (ToJson t1, ToJson t2, ToJson t3) => ToJson (t1, t2, t3) where toJson (x1, x2, x3) = JArray [toJson x1, toJson x2, toJson x3] instance (FromJson t1, FromJson t2, FromJson t3) => FromJson (t1, t2, t3) where safeFromJson (JArray [x1, x2, x3]) = return (fromJson x1, fromJson x2, fromJson x3) safeFromJson x = mkError x instance (ToJson t1, ToJson t2, ToJson t3, ToJson t4) => ToJson (t1, t2, t3, t4) where toJson (x1, x2, x3, x4) = JArray [toJson x1, toJson x2, toJson x3, toJson x4] instance (FromJson t1, FromJson t2, FromJson t3, FromJson t4) => FromJson (t1, t2, t3, t4) where safeFromJson (JArray [x1, x2, x3, x4]) = return (fromJson x1, fromJson x2, fromJson x3, fromJson x4) safeFromJson x = mkError x instance (ToJson t1, ToJson t2, ToJson t3, ToJson t4, ToJson t5) => ToJson (t1, t2, t3, t4, t5) where toJson (x1, x2, x3, x4, x5) = JArray [toJson x1, toJson x2, toJson x3, toJson x4, toJson x5] instance (FromJson t1, FromJson t2, FromJson t3, FromJson t4, FromJson t5) => FromJson (t1, t2, t3, t4, t5) where safeFromJson (JArray [x1, x2, x3, x4, x5]) = return (fromJson x1, fromJson x2, fromJson x3, fromJson x4, fromJson x5) safeFromJson x = mkError x