{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE RankNTypes #-} module Data.Aeson.Picker ( (|--) , (|-?) ) where import Control.Lens (Traversal', (^?)) import Data.Aeson (FromJSON (..), Result (..), Value, fromJSON) import Data.Aeson.Lens (AsValue, key, _Value) import Data.Maybe (fromMaybe) import Data.Text (Text) -- | From given JSON and selectors returns typed field. If input JSON is not valid or selected field is not found then error is thrown. -- If you need more safe way use '(|-?)' instead. Examples: -- -- > ghci> "5" |-- [] :: Int -- > 5 -- > ghci> "5" |-- [] :: Float -- > 5.0 -- > ghci>"5" |-- [] :: String -- > *** Exception: Data.Aeson.Picker: could not pick field with path: [] -- > -- > ghci> :set -XOverloadedStrings -- > ghci> "{\"a\": 5}" |-- ["a"] :: Int -- > 5 -- > ghci> "{\"a\": 5}" |-- ["b"] :: Int -- > *** Exception: Data.Aeson.Picker: could not pick field with path: ["b"] -- > ghci> "{\"outer\": {\"inner\": [1,2,3]}}" |-- ["outer", "inner"] :: [Int] -- > [1,2,3] -- > ghci> {\"outer\": {\"inner\": [1,2,3]}}" |-- ["outer", "inner"] :: [Double] -- > [1.0,2.0,3.0] -- > ghci> "{a: 5}" |-- ["a"] :: Int -- > *** Exception: Data.Aeson.Picker: input json is not valid infix 5 |-- (|--) :: (AsValue t, FromJSON a) => t -> [Text] -> a json |-- selectors = fromMaybe (error $ "Data.Aeson.Picker: could not pick field with path: " ++ show selectors) $ json |-? selectors -- | From given JSON and selectors returns typed field inside 'Maybe'. If input JSON is not valid then error is thrown. -- Examples: -- -- > ghci> "5" |-? [] :: Maybe Int -- > Just 5 -- > ghci> "5" |-? [] :: Maybe String -- > Nothing -- > ghci> "{\"a\": 5}" |-? ["a"] :: Maybe Int -- > Just 5 -- > ghci> "{a: 5}" |-? ["a"] :: Maybe Int -- > *** Exception: Data.Aeson.Picker: input json is not valid infix 5 |-? (|-?) :: (AsValue t, FromJSON a) => t -> [Text] -> Maybe a json |-? selectors = let validJSON = checkValidity json in pick validJSON selectors >>= convert -- | Checks validity for JSON format. Throws error if it is not valid. checkValidity :: AsValue t => t -> t checkValidity json = fromMaybe (error "Data.Aeson.Picker: input json is not valid") (json ^? _Value) `seq` json -- | Picks from given JSON selected field pick :: AsValue t => t -> [Text] -> Maybe Value pick json [] = json ^? _Value pick json selectors = json ^? genGetter selectors -- | Converts from 'Value' convert :: FromJSON a => Value -> Maybe a convert value = case fromJSON value of Success r -> Just r Error _ -> Nothing -- | Generates getter from given selectors genGetter :: AsValue t => [Text] -> Traversal' t Value genGetter [] = error "Data.Aeson.Picker.Internal.Functions.genGetter: this should not be happened" genGetter [x] = key x genGetter (x:xs) = key x . genGetter xs