aeson-t-0.0.2: Data/Aeson/Transform.hs
module Data.Aeson.Transform
(
-- * Example usage
-- $use
-- * Transformation builder
Builder(..)
-- * Executing transformation
, transform
) where
import Data.Aeson as A
import Data.Text
import qualified Data.Vector as Vec
import qualified Data.HashMap.Strict as H
-- | Transformations are specified by creating 'Builder' instances.
-- Builders specify how to navigate through input JSON and construct
-- output at various nodes in the tree.
--
data Builder =
Id -- ^ Pass input directly as output
| At Text Builder -- ^ Move to value in current object
| Attr Text -- ^ Get value in current object
| Keep [Text] -- ^ Filter current object by keys
| Map Builder -- ^ Map over input array
| Index Int -- ^ Get value at index of current array
| AtIndex Int Builder -- ^ Move to index in current array
| Obj (H.HashMap Text Builder) -- ^ Produce object with given keys
| Merge Builder Builder -- ^ Combine two objects
-- | Generates new Aeson 'Value' guided by a 'Builder'
--
transform :: Builder -> Value -> Value
transform Id x = x
transform (At k b) (A.Object o) = transform b $ o H.! k
transform (At _ _) _ = error "Expecting object (At)"
transform (AtIndex i b) (Array a) = transform b $ a Vec.! i
transform (AtIndex _ _) _ = error "Expecting array (AtIndex)"
transform (Index i) (Array a) = a Vec.! i
transform (Index _) _ = error "Expecting array (Index)"
transform (Map b) (Array a) = Array $ Vec.map (transform b) a
transform (Map _) _ = error "Expecting array (Map)"
transform (Attr k) (A.Object o) = o H.! k
transform (Attr _) _ = error "Expecting object (Keep)"
transform (Keep ks) (A.Object o) = A.Object $ H.filterWithKey (const . (`elem` ks)) o
transform (Keep _) _ = error "Expecting object (Keep)"
transform (Obj fs) x = A.Object $ H.map (`transform` x) fs
transform (Merge a b) x = case (transform a x, transform b x) of
(Object c, Object d) -> Object $ H.union c d
_ -> error "Expected two objects (Merge)"
-- $use
--
-- Filter unwanted attributes from an object
--
-- > Keep ["nice", "good"]
-- >
-- > -- { bad: 3, good: 1, nice: 500, evil: -3 }
-- > -- => { good: 1, nice: 500 }
--
-- Grab value
--
-- > Attr "foo"
-- >
-- > -- { foo: 2 } => 2
--
-- Dig deeper
--
-- > At "foo" $ Attr "bar"
-- >
-- > -- { foo: { bar: 3 }} => 3
--
-- Map stuff
--
-- > Map $ Attr "foo"
-- >
-- > -- [{ foo:1, foo:2 }] => [1, 2]
--
-- Extract indices
--
-- > Map $ Index 0
-- >
-- > -- [[1,2], [3,4]] => [1, 3]
--
-- Create object
--
-- > Obj $ fromList [
-- > ("first", Index 0)
-- > , ("second", Index 1)
-- > ]
-- >
-- > -- ["hi", "bye"] => { first:"hi", second:"bye" }
--
-- Transform position
--
-- > At "ranks" $ AtIndex 0 $ Attr "name"
-- >
-- > -- {ranks: [ { name:"winner", score:12 }, { name:"Loser", score: 3 ]}
-- > -- => "winner"
--
-- Combine objects
--
-- > Merge (Attr "foo") (Attr "bar")
-- >
-- > -- { foo: { a:1, b:2 }, bar: { b:3, c:4 } }
-- > -- => { a:1, b:2, c:4 }