Quickson-0.1: src/Data/Quickson.hs
{-# LANGUAGE OverloadedStrings #-}
module Data.Quickson where
import Control.Monad
import Control.Applicative
import Data.Aeson
import qualified Data.Aeson.Types as AT
import Data.Attoparsec.ByteString.Char8 as A8
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C8
import qualified Data.Text as T
-- | Quickson intermediary representation
data Quickson = Ob [(T.Text, Bool, Quickson)]
| Li Quickson
| Va deriving (Show)
-- | Parse a quickson structure
quicksonParse :: BS.ByteString -> Either String Quickson
quicksonParse = parseOnly parseStructure
where
parseStructure = parseObject <|> parseArray
parseObject = Ob <$> ("{" *> sepBy1 parseLookups (char ',') <* "}")
parseArray = Li <$> ("[" *> parseStructure <* "]")
parseLookups = (,,) <$> (b2t <$> takeWhile1 (notInClass "?,:}"))
<*> ("?" *> pure True <|> pure False)
<*> (":" *> parseStructure <|> pure Va)
b2t = T.pack . C8.unpack
-- | Execute a quickson structure against a value
quicksonExecute :: FromJSON a => Quickson -> Value -> Either String a
quicksonExecute que = AT.parseEither (drill que >=> parseJSON)
where
drill :: Quickson -> Value -> AT.Parser Value
drill (Ob [l]) = parseJSON >=> flip look l
drill (Ob keys) = parseJSON >=> forM keys . look >=> return . toJSON
drill (Li q) = parseJSON >=> mapM (drill q) >=> return . toJSON
drill Va = return
look v (k,True,qq) = v.:?k >>= maybe (return Null) (drill qq)
look v (k,False,qq) = v.:k >>= drill qq
-- | Perform a JSON extraction
quickson :: FromJSON a => BS.ByteString -> BS.ByteString -> Either String a
quickson structureSpec bs = do
val <- eitherDecodeStrict bs
query <- quicksonParse structureSpec
quicksonExecute query val