aeson-picker 0.1.0.0 → 0.1.0.1
raw patch · 5 files changed
+159/−47 lines, 5 filesdep ~aesondep ~hspecdep ~lensPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: aeson, hspec, lens, lens-aeson, text
API changes (from Hackage documentation)
Files
- CHANGELOG.md +6/−0
- README.md +73/−0
- aeson-picker.cabal +7/−8
- src/Data/Aeson/Picker.hs +73/−1
- src/Data/Aeson/Picker/Internal/Functions.hs +0/−38
CHANGELOG.md view
@@ -5,6 +5,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased]+## Added+- Error message is more useful.+- Examples.++### Changed+- Documentation added. ## [0.1.0.0] - 2018-01-22 ### Added
README.md view
@@ -1,3 +1,76 @@ # aeson-picker +[](https://travis-ci.org/ozzzzz/aeson-picker)+[](https://hackage.haskell.org/package/aeson-picker)+[](https://hackage.haskell.org/package/aeson-picker)+ Tiny library to get fields from JSON format++Common use is the following:+```+JSON |-- FIELDS :: EXPECTED TYPE+Text |-- [Text] :: a+```+So operator `(|--)` gets JSON (represented as `Text`), "route" to field inside JSON (represented as `[Text]`), and tries to parse field from JSON with described route to expected type.+If expected type can be deduced with type checker then it can be dropped down.++A little bit safer operator is `(|-?)`. It returns not `a` but `Maybe a`.++## Examples+First, add extension (to not pack `String` to `Text` every time):+```+ghci>:set -XOverloadedStrings+```++Then you can try something simple (empty list means that you try to parse `Value` from the top JSON-level):+```+ghci>"5" |-- [] :: Int+5+ghci>"5" |-- [] :: Float+5.0+ghci>"5" |-- [] :: String+"*** Exception: Data.Aeson.Picker: could not pick field with path: []+```++But what if field you are looking for somewhere inside JSON?+That's why are you here.++Let's try to get something from inside JSON:+```+ghci>"{\"a\": 5}" |-- ["a"] :: Int+5+```+But be sure that the field is presented inside JSON:+```+ghci>"{\"a\": 5}" |-- ["b"] :: Int+*** Exception: Data.Aeson.Picker: could not pick field with path: ["b"]+```+We can go deeper (as deep as you want):+```+ghci>"{\"outer\": {\"inner\": [1,2,3]}}" |-- ["outer", "inner"] :: [Int]+[1,2,3]+```+But be sure that you JSON is really valid (by specification key in JSON should be `String`):+```+ghci>"{a: 5}" |-- ["a"] :: Int+*** Exception: Data.Aeson.Picker: input json is not valid+```++If you want more "safe" picker, you can use another operator:+```+ghci>"5" |-? [] :: Maybe Int+Just 5+ghci>"{\"a\": 5}" |-? ["a"] :: Maybe Int+Just 5+ghci>"{\"a\": 5}" |-? ["b"] :: Maybe Int+Nothing+```++In current logic even operator `(|-?)` will throw error if JSON is not valid:+```+ghci>"{a: 5}" |-? ["a"] :: Maybe Int+*** Exception: Data.Aeson.Picker: input json is not valid+```++You can open issue if you do not think that it is right logic.+
aeson-picker.cabal view
@@ -1,5 +1,5 @@ name: aeson-picker-version: 0.1.0.0+version: 0.1.0.1 description: Tiny library to get fields from JSON format homepage: https://github.com/ozzzzz/aeson-picker#readme bug-reports: https://github.com/ozzzzz/aeson-picker/issues@@ -22,14 +22,13 @@ location: https://github.com/ozzzzz/aeson-picker library- hs-source-dirs: src+ hs-source-dirs: src exposed-modules: Data.Aeson.Picker- other-modules: Data.Aeson.Picker.Internal.Functions build-depends: base >=4.7 && <5- , aeson- , lens- , lens-aeson- , text+ , aeson >= 1.2 && < 1.3+ , lens >= 4.15 && < 5+ , lens-aeson >= 1.0 && < 1.1+ , text >= 1.2 && < 1.3 default-language: Haskell2010 test-suite aeson-picker-test@@ -39,6 +38,6 @@ ghc-options: -threaded -rtsopts -with-rtsopts=-N build-depends: base >=4.7 && <5 , aeson-picker- , hspec+ , hspec >= 2.4 && < 2.5 , text default-language: Haskell2010
src/Data/Aeson/Picker.hs view
@@ -1,6 +1,78 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE RankNTypes #-} module Data.Aeson.Picker ( (|--) , (|-?) ) where -import Data.Aeson.Picker.Internal.Functions ((|--), (|-?))+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:++-- >>> "5" |-- [] :: Int+-- 5+-- >>> "5" |-- [] :: Float+-- 5.0+-- >>> "5" |-- [] :: String+-- *** Exception: Data.Aeson.Picker: could not pick field with path: []+--+-- >>> :set -XOverloadedStrings+-- >>> "{\"a\": 5}" |-- ["a"] :: Int+-- 5+-- >>> "{\"a\": 5}" |-- ["b"] :: Int+-- *** Exception: Data.Aeson.Picker: could not pick field with path: ["b"]+-- >>> "{\"outer\": {\"inner\": [1,2,3]}}" |-- ["outer", "inner"] :: [Int]+-- [1,2,3]+-- >>> {\"outer\": {\"inner\": [1,2,3]}}" |-- ["outer", "inner"] :: [Double]+-- [1.0,2.0,3.0]+-- >>> "{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:++-- >>> "5" |-? [] :: Maybe Int+-- Just 5+-- >>> "5" |-? [] :: Maybe String+-- Nothing+-- >>> "{\"a\": 5}" |-? ["a"] :: Maybe Int+-- Just 5+-- >>> "{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
− src/Data/Aeson/Picker/Internal/Functions.hs
@@ -1,38 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE RankNTypes #-}--module Data.Aeson.Picker.Internal.Functions- ( (|--)- , (|-?)- ) where--import Control.Lens (Traversal', (^?))-import Data.Aeson (FromJSON (..), Result (..), Value, fromJSON)-import Data.Aeson.Lens -import Data.Maybe (fromJust)-import Data.Text (Text)--infix 5 |---(|--) :: (AsValue t, FromJSON a) => t -> [Text] -> a-json |-- [] = getFromValue . fromJust $ json ^? _Value-json |-- selectors = getFromValue . fromJust $ json ^? genGetter selectors--infix 5 |-?-(|-?) :: (AsValue t, FromJSON a) => t -> [Text] -> Maybe a-json |-? [] = (json ^? _Value) >>= getFromValueM-json |-? selectors = (json ^? genGetter selectors) >>= getFromValueM--genGetter :: AsValue t => [Text] -> Traversal' t Value-genGetter [] = error "selector should has at least one field"-genGetter [x] = key x-genGetter (x:xs) = key x . genGetter xs--getFromValue :: FromJSON a => Value -> a-getFromValue value = case fromJSON value of- Success r -> r- Error e -> error e--getFromValueM :: FromJSON a => Value -> Maybe a-getFromValueM value = case fromJSON value of- Success r -> Just r- Error e -> Nothing