{-# LANGUAGE CPP #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DefaultSignatures #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
#if __GLASGOW_HASKELL__ >= 800
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}
#endif
module Data.Aeson.Lens
(
AsNumber(..)
, _Integral
, nonNull
, Primitive(..)
, AsPrimitive(..)
, AsValue(..)
, key, members
, nth, values
, AsJSON(..)
, _JSON'
#if __GLASGOW_HASKELL__ >= 800
, pattern JSON
, pattern Value_
, pattern Number_
, pattern Double
, pattern Integer
, pattern Integral
, pattern Primitive
, pattern Bool_
, pattern String_
, pattern Null_
#endif
) where
import Control.Applicative
import Control.Lens
import Data.Aeson
import Data.Aeson.Parser (value)
import Data.Attoparsec.ByteString.Lazy (maybeResult, parse)
import Data.Scientific (Scientific)
import qualified Data.Scientific as Scientific
import qualified Data.ByteString as Strict
import Data.ByteString.Lazy.Char8 as Lazy hiding (putStrLn)
import Data.Data
import Data.HashMap.Strict (HashMap)
import Data.Text as Text
import qualified Data.Text.Lazy as LazyText
import Data.Text.Lens (packed)
import qualified Data.Text.Encoding as StrictText
import qualified Data.Text.Lazy.Encoding as LazyText
import Data.Vector (Vector)
import Prelude hiding (null)
class AsNumber t where
_Number :: Prism' t Scientific
default _Number :: AsPrimitive t => Prism' t Scientific
_Number = _Primitive._Number
{-# INLINE _Number #-}
_Double :: Prism' t Double
_Double = _Number.iso Scientific.toRealFloat realToFrac
{-# INLINE _Double #-}
_Integer :: Prism' t Integer
_Integer = _Number.iso floor fromIntegral
{-# INLINE _Integer #-}
instance AsNumber Value where
_Number = prism Number $ \v -> case v of Number n -> Right n; _ -> Left v
{-# INLINE _Number #-}
instance AsNumber Scientific where
_Number = id
{-# INLINE _Number #-}
instance AsNumber Strict.ByteString
instance AsNumber Lazy.ByteString
instance AsNumber Text
instance AsNumber LazyText.Text
instance AsNumber String
_Integral :: (AsNumber t, Integral a) => Prism' t a
_Integral = _Number . iso floor fromIntegral
{-# INLINE _Integral #-}
data Primitive
= StringPrim !Text
| NumberPrim !Scientific
| BoolPrim !Bool
| NullPrim
deriving (Eq,Ord,Show,Data,Typeable)
instance AsNumber Primitive where
_Number = prism NumberPrim $ \v -> case v of NumberPrim s -> Right s; _ -> Left v
{-# INLINE _Number #-}
class AsNumber t => AsPrimitive t where
_Primitive :: Prism' t Primitive
default _Primitive :: AsValue t => Prism' t Primitive
_Primitive = _Value._Primitive
{-# INLINE _Primitive #-}
_String :: Prism' t Text
_String = _Primitive.prism StringPrim (\v -> case v of StringPrim s -> Right s; _ -> Left v)
{-# INLINE _String #-}
_Bool :: Prism' t Bool
_Bool = _Primitive.prism BoolPrim (\v -> case v of BoolPrim b -> Right b; _ -> Left v)
{-# INLINE _Bool #-}
_Null :: Prism' t ()
_Null = _Primitive.prism (const NullPrim) (\v -> case v of NullPrim -> Right (); _ -> Left v)
{-# INLINE _Null #-}
instance AsPrimitive Value where
_Primitive = prism fromPrim toPrim
where
toPrim (String s) = Right $ StringPrim s
toPrim (Number n) = Right $ NumberPrim n
toPrim (Bool b) = Right $ BoolPrim b
toPrim Null = Right NullPrim
toPrim v = Left v
{-# INLINE toPrim #-}
fromPrim (StringPrim s) = String s
fromPrim (NumberPrim n) = Number n
fromPrim (BoolPrim b) = Bool b
fromPrim NullPrim = Null
{-# INLINE fromPrim #-}
{-# INLINE _Primitive #-}
_String = prism String $ \v -> case v of String s -> Right s; _ -> Left v
{-# INLINE _String #-}
_Bool = prism Bool (\v -> case v of Bool b -> Right b; _ -> Left v)
{-# INLINE _Bool #-}
_Null = prism (const Null) (\v -> case v of Null -> Right (); _ -> Left v)
{-# INLINE _Null #-}
instance AsPrimitive Strict.ByteString
instance AsPrimitive Lazy.ByteString
instance AsPrimitive Text.Text
instance AsPrimitive LazyText.Text
instance AsPrimitive String
instance AsPrimitive Primitive where
_Primitive = id
{-# INLINE _Primitive #-}
nonNull :: Prism' Value Value
nonNull = prism id (\v -> if isn't _Null v then Right v else Left v)
{-# INLINE nonNull #-}
class AsPrimitive t => AsValue t where
_Value :: Prism' t Value
_Object :: Prism' t (HashMap Text Value)
_Object = _Value.prism Object (\v -> case v of Object o -> Right o; _ -> Left v)
{-# INLINE _Object #-}
_Array :: Prism' t (Vector Value)
_Array = _Value.prism Array (\v -> case v of Array a -> Right a; _ -> Left v)
{-# INLINE _Array #-}
instance AsValue Value where
_Value = id
{-# INLINE _Value #-}
instance AsValue Strict.ByteString where
_Value = _JSON
{-# INLINE _Value #-}
instance AsValue Lazy.ByteString where
_Value = _JSON
{-# INLINE _Value #-}
instance AsValue String where
_Value = strictUtf8._JSON
{-# INLINE _Value #-}
instance AsValue Text where
_Value = strictTextUtf8._JSON
{-# INLINE _Value #-}
instance AsValue LazyText.Text where
_Value = lazyTextUtf8._JSON
{-# INLINE _Value #-}
key :: AsValue t => Text -> Traversal' t Value
key i = _Object . ix i
{-# INLINE key #-}
members :: AsValue t => IndexedTraversal' Text t Value
members = _Object . itraversed
{-# INLINE members #-}
nth :: AsValue t => Int -> Traversal' t Value
nth i = _Array . ix i
{-# INLINE nth #-}
values :: AsValue t => IndexedTraversal' Int t Value
values = _Array . traversed
{-# INLINE values #-}
strictUtf8 :: Iso' String Strict.ByteString
strictUtf8 = packed . strictTextUtf8
strictTextUtf8 :: Iso' Text.Text Strict.ByteString
strictTextUtf8 = iso StrictText.encodeUtf8 StrictText.decodeUtf8
lazyTextUtf8 :: Iso' LazyText.Text Lazy.ByteString
lazyTextUtf8 = iso LazyText.encodeUtf8 LazyText.decodeUtf8
_JSON' :: (AsJSON t, FromJSON a, ToJSON a) => Prism' t a
_JSON' = _JSON
class AsJSON t where
_JSON :: (FromJSON a, ToJSON b) => Prism t t a b
instance AsJSON Strict.ByteString where
_JSON = lazy._JSON
{-# INLINE _JSON #-}
instance AsJSON Lazy.ByteString where
_JSON = prism' encode decodeValue
where
decodeValue :: (FromJSON a) => Lazy.ByteString -> Maybe a
decodeValue s = maybeResult (parse value s) >>= \x -> case fromJSON x of
Success v -> Just v
_ -> Nothing
{-# INLINE _JSON #-}
instance AsJSON String where
_JSON = strictUtf8._JSON
{-# INLINE _JSON #-}
instance AsJSON Text where
_JSON = strictTextUtf8._JSON
{-# INLINE _JSON #-}
instance AsJSON LazyText.Text where
_JSON = lazyTextUtf8._JSON
{-# INLINE _JSON #-}
instance AsJSON Value where
_JSON = prism toJSON $ \x -> case fromJSON x of
Success y -> Right y;
_ -> Left x
{-# INLINE _JSON #-}
type instance Index Value = Text
type instance IxValue Value = Value
instance Ixed Value where
ix i f (Object o) = Object <$> ix i f o
ix _ _ v = pure v
{-# INLINE ix #-}
instance Plated Value where
plate f (Object o) = Object <$> traverse f o
plate f (Array a) = Array <$> traverse f a
plate _ xs = pure xs
{-# INLINE plate #-}
#if __GLASGOW_HASKELL__ >= 800
pattern JSON :: (FromJSON a, ToJSON a, AsJSON t) => () => a -> t
pattern JSON a <- (preview _JSON -> Just a) where
JSON a = _JSON # a
pattern Value_ :: (FromJSON a, ToJSON a) => () => a -> Value
pattern Value_ a <- (fromJSON -> Success a) where
Value_ a = toJSON a
pattern Number_ :: AsNumber t => Scientific -> t
pattern Number_ n <- (preview _Number -> Just n) where
Number_ n = _Number # n
pattern Double :: AsNumber t => Double -> t
pattern Double d <- (preview _Double -> Just d) where
Double d = _Double # d
pattern Integer :: AsNumber t => Integer -> t
pattern Integer i <- (preview _Integer -> Just i) where
Integer i = _Integer # i
pattern Integral :: (AsNumber t, Integral a) => a -> t
pattern Integral d <- (preview _Integral -> Just d) where
Integral d = _Integral # d
pattern Primitive :: AsPrimitive t => Primitive -> t
pattern Primitive p <- (preview _Primitive -> Just p) where
Primitive p = _Primitive # p
pattern Bool_ :: AsPrimitive t => Bool -> t
pattern Bool_ b <- (preview _Bool -> Just b) where
Bool_ b = _Bool # b
pattern String_ :: AsPrimitive t => Text -> t
pattern String_ p <- (preview _String -> Just p) where
String_ p = _String # p
pattern Null_ :: AsPrimitive t => t
pattern Null_ <- (preview _Null -> Just ()) where
Null_ = _Null # ()
#endif