I don’t think introducing lenses, prefacing the post with language extensions, and using TH for a multiline string amounts to “easy json parsing.” Here’s a stub using only base and aeson to implement an off-brand jq in 35 lines.
import Data.Char (isDigit)
import Data.Foldable (toList)
import Data.String (fromString)
import System.Environment (getArgs)
import Data.Aeson
import Data.Aeson.Types
import Data.ByteString.Lazy as BS (ByteString, interact)
main :: IO ()
main = do
args <- getArgs
BS.interact (either error encode . decodeAndSearch args)
decodeAndSearch :: [String] -> ByteString -> Either String Value
decodeAndSearch args bytes = do
val <- eitherDecode bytes
parseEither (search args) val
search :: [String] -> Value -> Parser Value
search [] val = return val
search (arg:args) val =
case arg of
'_':ds | all isDigit ds ->
flip (withArray "Arr") val $ \arr -> do
let idx = read ds
xs = toList arr
if idx < length xs
then search args (xs !! idx)
else fail $ "Index out of bounds for array: " ++ show (idx, length xs)
'?':key ->
flip (withObject "Obj") val $ \obj -> do
fld <- parseField obj (fromString key)
search args fld
_ -> fail $ "Use !<digits> to index arrays or ?<key> to lookup in an object. Saw: " ++ show arg
I don’t think introducing lenses, prefacing the post with language extensions, and using TH for a multiline string amounts to “easy json parsing.” Here’s a stub using only
base
andaeson
to implement an off-brand jq in 35 lines.jq is off-brand lens!