packages feed

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 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 +[![Travis](https://img.shields.io/travis/ozzzzz/aeson-picker.svg)](https://travis-ci.org/ozzzzz/aeson-picker)+[![hackage](https://img.shields.io/hackage/v/aeson-picker.svg)](https://hackage.haskell.org/package/aeson-picker)+[![hackage-deps](https://img.shields.io/hackage-deps/v/aeson-picker.svg)](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