Easiest way to enter a JSON literal in Haskell ghci - json

I'm testing out some functions in ghci before I put them into a project, and I'm straining to find the simplest way to enter json literals in the ghci without a ton of boilerplate
For example, I'd like to try out:
myFuncThatTakesAJson
on inputs
x = {}
y = {"a": 1}
But when I try entering these:
import Text.JSON
Prelude> x = parseJSON "{}"
Prelude> y = parseJSON "{'a' : 1}"
I see errors like:
<interactive>:17:5: error:
Variable not in scope: parseJSON :: [Char] -> t
y = fromJSON "{'a' : 1}"
<interactive>:20:5: error:
Data constructor not in scope: FromJSON :: [Char] -> t
Is there any way to allow json objects to be entered as strings without defining full data types with custom FromJSON instances?

There sure is! The type of a raw JSON value is Value. With that in mind, here's a couple of ways of constructing a Value in GHCi:
with parseJSON: since Value also has a FromJSON instance, you can use decode :: FromJSON a => ByteString -> Maybe a on it. Note that aeson is encouraging you to use ByteString instead of the inefficient default String. You can gloss over this detail by making string literals be overloaded by turning on -XOverloadedStrings:
ghci> import Data.ByteString
ghci> import Data.Aeson
ghci> :set -XOverloadedStrings
ghci> x = decode "{}" :: Maybe Value
x :: Maybe Value
ghci> y = decode "{\"a\" : 1}" :: Maybe Value -- note: single quotes won't work
y :: Maybe Value
with quasiquotes: you can also make use of the aesonQQ quasiquoter to get compile time JSON literals (this won't matter much in the REPL, beyond looking nicer, but in real code it means no actual parsing at runtime):
ghci> import Data.Aeson.QQ.Simple
ghci> :set -XQuasiQuotes
ghci> x = [aesonQQ| {} |]
x :: Value
ghci> y = [aesonQQ| {"a" : 1} |]
y :: Value

Related

Reading nested JSON data encoded as a nested string with Aeson

I have this weird JSON to parse containing nested JSON ... a string. So instead of
{\"title\": \"Lord of the rings\", \"author\": {\"666\": \"Tolkien\"}\"}"
I have
{\"title\": \"Lord of the rings\", \"author\": \"{\\\"666\\\": \\\"Tolkien\\\"}\"}"
Here's my (failed) attempt to parse the nested string using decode, inside an instance of FromJSON :
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.Maybe
import GHC.Generics
import Data.Aeson
import qualified Data.Map as M
type Authors = M.Map Int String
data Book = Book
{
title :: String,
author :: Authors
}
deriving (Show, Generic)
decodeAuthors x = fromJust (decode x :: Maybe Authors)
instance FromJSON Book where
parseJSON = withObject "Book" $ \v -> do
t <- v .: "title"
a <- decodeAuthors <?> v .: "author"
return $ Book t a
jsonTest = "{\"title\": \"Lord of the rings\", \"author\": \"{\\\"666\\\": \\\"Tolkien\\\"}\"}"
test = decode jsonTest :: Maybe Book
Is there a way to decode the whole JSON in a single pass ? Thanks !
A couple problems here.
First, your use of <?> is nonsensical. I'm going to assume it's a typo, and what you actually meant was <$>.
Second, the type of decodeAuthors is ByteString -> Authors, which means its parameter is of type ByteString, which means that the expression v .: "author" must be of type Parser ByteString, which means that there must be an instance FromJSON ByteString, but such instance doesn't exists (for reasons that escape me at the moment).
What you actually want is for v .: "author" to return a Parser String (or perhaps Parser Text), and then have decodeAuthors accept a String and convert it to ByteString (using pack) before passing to decode:
import Data.ByteString.Lazy.Char8 (pack)
decodeAuthors :: String -> Authors
decodeAuthors x = fromJust (decode (pack x) :: Maybe Authors)
(also note: it's a good idea to give you declarations type signatures that you think they should have. This lets the compiler point out errors earlier)
Edit:
As #DanielWagner correctly points out, pack may garble Unicode text. If you want to handle it correctly, use Data.ByteString.Lazy.UTF8.fromString from utf8-string to do the conversion:
import Data.ByteString.Lazy.UTF8 (fromString)
decodeAuthors :: String -> Authors
decodeAuthors x = fromJust (decode (fromString x) :: Maybe Authors)
But in that case you should also be careful about the type of jsonTest: the way your code is written, its type would be ByteString, but any non-ASCII characters that may be inside would be cut off because of the way IsString works. To preserve them, you need to use the same fromString on it:
jsonTest = fromString "{\"title\": \"Lord of the rings\", \"author\": \"{\\\"666\\\": \\\"Tolkien\\\"}\"}"

Parsing JSON into RoseTree (recursive parsing)

I am struggling with a function that would take String JSON as an input and return RoseTree structure as an output.
My code is as follows:
import qualified Data.Aeson as JSON
import qualified Data.Text as T
import qualified Data.Aeson.Types as AT
import Control.Applicative
import qualified Data.ByteString.Char8 as BS
import Data.Attoparsec.ByteString.Char8
data RoseTree a = Empty | RoseTree a [RoseTree a]
deriving (Show)
instance (Show a) => JSON.ToJSON (RoseTree a) where
toJSON (RoseTree n cs) =
JSON.object [T.pack "value" JSON..= show n
, T.pack "children" JSON..= JSON.toJSON cs]
instance (Show a, JSON.FromJSON a) => JSON.FromJSON (RoseTree a) where
parseJSON (JSON.Object o) =
RoseTree <$> o JSON..: T.pack "value"
<*> o JSON..: T.pack "children"
parseRoseTreeFromJSON :: (Show a, JSON.FromJSON a) => String -> (RoseTree a)
parseRoseTreeFromJSON json =
let bs = BS.pack json in case parse JSON.json bs of
(Done rest r) -> case AT.parseMaybe JSON.parseJSON r of
(Just x) -> x
Nothing -> Empty
_ -> Empty
It converts RoseTree structure into JSON perfectly fine. For example, when I run JSON.encode $ JSON.toJSON $ RoseTree 1 [], it returns "{\"children\":[],\"value\":\"1\"}", running JSON.encode $ JSON.toJSON $ RoseTree 1 [RoseTree 2 []] returns "{\"children\":[{\"children\":[],\"value\":\"2\"}],\"value\":\"1\"}".
But when I try to supply that JSON I have got on the previous step into the parser, it always returns Empty:
*Main> parseRoseTreeFromJSON "{\"children\":[],\"value\":1}"
Empty
*Main> parseRoseTreeFromJSON "{\"children\":[{\"children\":[],\"value\":\"2\"}],\"value\":\"1\"}"
Empty
Similarly,
*Main> JSON.decode $ BLS.pack "{\"children\":[{\"children\":[],\"value\":\"2\"}],\"value\":\"1\"}"
Nothing
I suspect that my parser is incorrectly defined. But I cannot tell what exactly is wrong. Is the approach I am using the correct one to approach recursive parsing? What would be the right way to do it recursively?
Weird GHCi typing rules strike again! Everything works as expected if you add type annotations:
ghci> :set -XOverloadedStrings
ghci> JSON.decode "{\"children\":[],\"value\":1}" :: Maybe (RoseTree Int)
Just (RoseTree 1 [])
Without knowing what type you are trying to parse (it sees FromJSON a => Maybe a), GHCi defaults a ~ (). You can try this out by testing the FromJSON () instance out and noticing that it succeeds!
ghci> JSON.decode "[]"
Just ()
Couple side notes on your code if this is actually for a project (and not just for fun and learning):
I encourage you to check out OverloadedStrings (you can basically drop almost all uses of T.pack from your code since string literals will infer whether they should have type String, lazy/strict Text, lazy/strict ByteString etc.)
You can use DeriveGeneric and DeriveAnyClass to get JSON parsing almost for free (although I concede the behavior will be slightly different from what you currently have)
With those suggestions, here is a rewrite of your code:
{-# LANGUAGE OverloadedStrings, DeriveGeneric, DeriveAnyClass #-}
import qualified Data.Aeson as JSON
import qualified Data.ByteString.Lazy.Char8 as BS
import GHC.Generics (Generic)
import Data.Maybe (fromMaybe)
data RoseTree a = RoseTree { value :: a, children :: [RoseTree a] }
deriving (Show, Generic, JSON.FromJSON, JSON.ToJSON)
Note that decode from Data.Aeson does (almost) what parseRoseTreeFromJSON does...
ghci> :set -XOverloadedStrings
ghci> JSON.encode (RoseTree 1 [])
"{\"children\":[],\"value\":1}"
ghci> JSON.encode (RoseTree 1 [RoseTree 2 []])
"{\"children\":[{\"children\":[],\"value\":2}],\"value\":1}"
ghci> JSON.decode "{\"children\":[],\"value\":1}" :: Maybe (RoseTree Int)
Just (RoseTree {value = 1, children = []})
ghci> JSON.decode "{\"children\":[{\"children\":[],\"value\":2}],\"value\":1}" :: Maybe (RoseTree Int)
Just (RoseTree {value = 1, children = [RoseTree {value = 2, children = []}]})

Recursively change a JSON data structure in Haskell

I am trying to write a function that will take a JSON object, make a change to every string value in it and return a new JSON object. So far my code is:
applyContext :: FromJSON a => a -> a
applyContext x =
case x of
Array _ -> map applyContext x
Object _ -> map applyContext x
String _ -> parseValue x
_ -> x
However, the compiler complains about second second case line:
Couldn't match expected type `[b0]' with actual type `a'
`a' is a rigid type variable bound by
the type signature for:
applyContext :: forall a. FromJSON a => a -> a
at app\Main.hs:43:17
I'm guessing that is because map is meant to work on lists, but I would have naively expected it to use Data.HashMap.Lazy.map instead, since that is what the type actually is in that case. If I explicitly use that function I get
Couldn't match expected type `HashMap.HashMap k0 v20' with actual type `a'
which also makes sense, since I haven't constrained a to that extent because then it wouldn't work for the other cases. I suspect that if I throw enough explicit types at this I could make it work but it feels like it should be a lot simpler. What is an idiomatic way of writing this function, or if this is good then what would be the simplest way of getting the types right?
First of all, what FromJSON a => a does mean? It's type of some thing what says: it can be thing with any type but only from class FromJSON. This class can contain types which very differently constructed and you can't do any pattern matching. You can only do what is specified in the class FromJSON declaration by programmer. Basically, there is one method parseJSON :: FromJSON a => Value -> Parser a.
Secondly, you should use some isomorphic representation of JSON for your work. The type Value is good one. So, you can do the main work by the function like Value -> Value. After that, you can compose this fuction with parseJSON and toJSON for generalse types.
Like this:
change :: Value -> Value
change (Array x) = Array . fmap change $ x
change (Object x) = Object . fmap change $ x
change (String x) = Object . parseValue $ x
change x = x
apply :: (ToJSON a, FromJSON b) => (Value -> Value) -> a -> Result b
apply change = fromJSON . change . toJSON
unsafeApply :: (ToJSON a, FromJSON b) => (Value -> Value) -> a -> b
unsafeApply change x = case apply change x of
Success x -> x
Error msg -> error $ "unsafeApply: " ++ msg
applyContext :: (ToJSON a, FromJSON b) => a -> b
applyContext = unsafeApply change
You can write more complicated transformations like Value -> Value with lens and lens-aeson. For example:
import Control.Lens
import Control.Monad.State
import Data.Aeson
import Data.Aeson.Lens
import Data.Text.Lens
import Data.Char
change :: Value -> Value
change = execState go
where
go = do
zoom values go
zoom members go
_String . _Text . each %= toUpper
_Bool %= not
_Number *= 10
main = print $ json & _Value %~ change
where json = "{\"a\":[1,\"foo\",false],\"b\":\"bar\",\"c\":{\"d\":5}}"
Output will be:
"{\"a\":[10,\"FOO\",true],\"b\":\"BAR\",\"c\":{\"d\":50}}"

Haskell aeson package basic usage

Working my way through Haskell and I'm trying to learn how to serialized to/from JSON.
I'm using aeson-0.8.0.2 & I'm stuck at basic decoding. Here's what I have:
file playground/aeson.hs:
{-# LANGUAGE OverloadedStrings #-}
import Data.Text
import Data.Aeson
data Person = Person
{ name :: Text
, age :: Int
} deriving Show
instance FromJSON Person where
parseJSON (Object v) = Person <$>
v .: "name" <*>
v .: "age"
parseJSON _ = mzero
main = do
let a = decode "{\"name\":\"Joe\",\"age\":12}" :: Maybe Person
print "aa"
ghc --make playground/aeson.hs yields:
[1 of 1] Compiling Main ( playground/aeson.hs,
playground/aeson.o )
playground/aeson.hs:13:35: Not in scope: `'
playground/aeson.hs:14:40: Not in scope: `<*>'
playground/aeson.hs:17:28: Not in scope: `mzero'
Any idea what I'm doing wrong?
Why is OverloadedString needed here?
Also, I have no idea what <$>, <*>, or mzero are supposed to mean; I'd appreciate tips on where I can read about any of these.
You need to import Control.Applicative and Control.Monad to get <$>, <*> and mzero. <$> just an infix operator for fmap, and <*> is the Applicative operator, you can think of it as a more generalized form of fmap for now. mzero is defined for the MonadPlus class, which is a class representing that a Monad has the operation
mplus :: m a -> m a -> m a
And a "monadic zero" element called mzero. The simplest example is for lists:
> mzero :: [Int]
[]
> [1, 2, 3] `mplus` [4, 5, 6]
[1, 2, 3, 4, 5, 6]
Here mzero is being used to represent a failure to parse. For looking up symbols in the future, I recommend using hoogle or FP Complete's version. Once you find the symbol, read the documentation, the source, and look around for examples of its use on the internet. You'll learn a lot by looking for it yourself, although it'll take you a little while to get used to this kind of research.
The OverloadedStrings extension is needed here because the Aeson library works with the Text type from Data.Text instead of the built-in String type. This extension lets you use string literals as Text instead of String, just as the numeric literal 0 can be an Int, Integer, Float, Double, Complex and other types. OverloadedStrings makes string literals have type Text.String.IsString s => s instead of just String, so it makes it easy to use alternate string-y types.
For <$> you need to import Control.Applicative and for mzero you need to import Control.Monad.
You can determine this by using the web-based version of hoogle (http://www.haskell.org/hoogle) or the command line version:
$ hoogle '<$>'
$ hoogle mzero

Streaming parsing of JSON in Haskell with Pipes.Aeson

The Pipes.Aeson library exposes the following function:
decode :: (Monad m, ToJSON a) => Parser ByteString m (Either DecodingError a)
If I use evalStateT with this parser and a file handle as an argument, a single JSON object is read from the file and parsed.
The problem is that the file contains several objects (all of the same type) and I'd like to fold or reduce them as they are read.
Pipes.Parse provides:
foldAll :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Parser a m b
but as you can see this returns a new parser - I can't think of a way of supplying the first parser as an argument.
It looks like a Parser is actually a Producer in a StateT monad transformer. I wondered whether there's a way of extracting the Producer from the StateT so that evalStateT can be applied to the foldAll Parser, and the Producer from the decode Parser.
This is probably completely the wrong approach though.
My question, in short:
When parsing a file using Pipes.Aeson, what's the best way to fold all the objects in the file?
Instead of using decode, you can use the decoded parsing lens from Pipes.Aeson.Unchecked. It turns a producer of ByteString into a producer of parsed JSON values.
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Pipes
import qualified Pipes.Prelude as P
import qualified Pipes.Aeson as A
import qualified Pipes.Aeson.Unchecked as AU
import qualified Data.ByteString as B
import Control.Lens (view)
byteProducer :: Monad m => Producer B.ByteString m ()
byteProducer = yield "1 2 3 4"
intProducer :: Monad m => Producer Int m (Either (A.DecodingError, Producer B.ByteString m ()) ())
intProducer = view AU.decoded byteProducer
The return value of intProducer is a bit scary, but it only means that intProducer finishes either with a parsing error and the unparsed bytes after the error, or with the return value of the original producer (which is () in our case).
We can ignore the return value:
intProducer' :: Monad m => Producer Int m ()
intProducer' = intProducer >> return ()
And plug the producer into a fold from Pipes.Prelude, like sum:
main :: IO ()
main = do
total <- P.sum intProducer'
putStrLn $ show total
In ghci:
λ :main
10
Note also that the functions purely and impurely let you apply to producers folds defined in the foldl package.