Problem decoding json from input args with Aeson - haskell

I'm trying to write a Haskell program that expects Json string provided as input. Expected Json is a list of Person objects. I want to handle a case when user did not provide input by defaulting to Json of empty list.
It seems like I'm running into type conversion issue between ByteString and String. I do have OverloadedStrings turned on but it doesn't seem to help here. Here's the simplified code.
{-# LANGUAGE OverloadedStrings #-}
import GHC.Generics
import System.Environment
import Data.Aeson
import Data.Maybe (fromMaybe, listToMaybe)
import Data.Text (Text)
data Person = Person { pName :: Text, pAge :: Int } deriving (Show,
Generic)
instance ToJSON Person
instance FromJSON Person
main :: IO (Maybe [Person])
main = do
args <- getArgs
arg1 <- pure $ (fromMaybe "[]" (listToMaybe args))
-- let arg1 = "[{\"pName\": \"James\", \"pAge\": 30}]"
return $ decode arg1 :: IO (Maybe [Person])
Error I'm getting is:
• Couldn't match type ‘[Char]’
with ‘Data.ByteString.Lazy.Internal.ByteString’
Expected type: Data.ByteString.Lazy.Internal.ByteString
Actual type: String
• In the first argument of ‘decode’, namely ‘arg1’
In the second argument of ‘($)’, namely ‘decode arg1’
In a stmt of a 'do' block:
return $ decode arg1 :: IO (Maybe [Person])
If I uncomment let arg1 to simulate what arg1 should be, then code compiles.

The problem is that getArgs returns a [String], not a [ByteString]. decode really wants a ByteString as input.
OverloadedStrings doesn't help with that; it only affects string literals in your code, not external inputs. That's why the version with the hardcoded argument (let arg1 = "[{\"pName\": \"James\", \"pAge\": 30}]") works: arg1 automatically becomes a ByteString to make decode arg1 work, but getArgs has an incompatible type.
One possible fix would be to encode the command line strings to bytes somehow, but there seems to be an easier alternative:
import System.Posix.Env.ByteString (getArgs)
which gives you a
getArgs :: IO [ByteString]
(I haven't actually tested this code; you may also have to use decodeStrict instead of decode.)

Related

Haskell type error occurs when compiling two code snippets together that separately raise no error

I am having a type issue with Haskell, the program below throws the compile time error:
Couldn't match expected type ‘bytestring-0.10.8.2:Data.ByteString.Lazy.Internal.ByteString’ with actual type ‘Text’
Program is:
{-# LANGUAGE OverloadedStrings #-}
module Main where
...
import Control.Concurrent (MVar, newMVar, modifyMVar_, modifyMVar, readMVar)
import qualified Data.Text as T
import qualified Data.Text.IO as T
import qualified Network.WebSockets as WS
import Data.Map (Map)
import Data.Aeson (decode)
...
application :: MVar ServerState -> WS.ServerApp
application state pending = do
conn <- WS.acceptRequest pending
msg <- WS.receiveData conn
-- EITHER this line can be included
T.putStrLn msg
-- OR these two lines, but not both
decodedObject <- return (decode msg :: Maybe (Map String Int))
print decodedObject
...
It seems to me that the basic issue is that putStrLn expects Text whereas decode expects Bytetring.
What I don't get is why I can run this section of the code:
T.putStrLn msg
Or I can run this section of the code:
decodedObject <- return (decode msg :: Maybe (Map String Int))
print decodedObject
But not both together.
What is the proper way to resolve this issue in the program?
I guess this is something like Type Coercion, or Type Inference, or what would be Casting in other languages. The problem is I don't know how to phrase the problem clearly enough to look it up.
It's as if msg can be one of a number of Types, but as soon as it is forced to be one Type, it can't then be another...
I'm also not sure if this overlaps with Overloaded strings. I have the pragma and am compiling with -XOverloadedStrings
I'm quite a newbie, so hope this is a reasonable question.
Any advice gratefully received! Thanks
This is because WS.receiveData is polymorphic on its return type:
receiveData :: WebSocketsData a => Connection -> IO a
it only needs to be WebSocketsData a instance, which both Text and ByteString are. So the compiler just infers the type.
I suggest you just assume it's a ByteString, and convert in Text upon the putStrLn usage.
Thanks to everyone for their advice. My final understanding is that any value in Haskell can be polymorphic until you force it to settle on a type, at which point it can't be any other type (stupid, but I hadn't seen a clear example of that before).
In my example, WS.receiveData returns polymorphic IO a where a is an instance of class WebsocketData which itself is parameterised by a type which can be either Text or Bytestring.
Aeson decode expects a (lazy) Bytestring. Assuming that we settle on (lazy) Bytestring for our a, this means the first line that I mentioned before needs to become:
T.putStrLn $ toStrict $ decodeUtf8 msg
to convert the lazy ByteString to a strict Text. I can do this so long as I know the incoming websocket message is UTF8 encoded.
I may have got some wording wrong there, but think that's basically it.

Haskell Scotty Webserver send a text response

I get a GET request and want to send a text message as a response to it.I have the following code but am getting the following error
{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty
import Network.Wai.Middleware.RequestLogger
import Data.Monoid (mconcat)
main = scotty 4000 $ do
middleware logStdoutDev
get "/" $ do
beam<- param "q"
text $ "The message which I want to send"
The error I am getting when I try to run the server is
No instance for (Parsable t0) arising from a use of ‘param’
The type variable ‘t0’ is ambiguous
Note: there are several potential instances:
instance Parsable () -- Defined in ‘scotty-0.9.1:Web.Scotty.Action’
instance Parsable Bool
-- Defined in ‘scotty-0.9.1:Web.Scotty.Action’
instance Parsable Data.ByteString.Lazy.Internal.ByteString
-- Defined in ‘scotty-0.9.1:Web.Scotty.Action’
...plus 9 others
In a stmt of a 'do' block: beam <- param "q"
In the second argument of ‘($)’, namely
‘do { beam <- param "q";
text $ "The message I want to send" }’
In a stmt of a 'do' block:
get "/"
$ do { beam <- param "q";
text $ "The message I want to send" }
param has type Parsable a => Text -> ActionM a. As you can see, a is polymorphic and only requires an instance of Parsable a. It's the return type as well. But as you can see in the documentation Parsable has several instances available! GHC doesn't know what you want, hence the ambiguous error. You will need to specify a type annotation letting GHC know what you want. But anyway, let's write the code
beam <- param "q" :: ActionM Text
should do the trick. Now beam should have be a Text. But maybe you are expecting your parameter to be an integer so something like
beam <- param "q" :: ActionM Integer
can work as well.

Replace nested pattern matching with "do" for Monad

I want to simplify this code
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
import Network.HTTP.Types
import Data.Text
getJSON :: String -> IO (Either String Value)
getJSON url = eitherDecode <$> simpleHttp url
--------------------------------------------------------------------
maybeJson <- getJSON "abc.com"
case maybeJson of
Right jsonValue -> case jsonValue of
(Object jsonObject) ->
case (HashMap.lookup "key123" jsonObject) of
(Just (String val)) -> Data.Text.IO.putStrLn val
_ -> error "Couldn't get the key"
_ -> error "Unexpected JSON"
Left errorMsg -> error $ "Error in parsing: " ++ errorMsg
by using do syntax for Monad
maybeJson <- getJSON "abc.com/123"
let toPrint = do
Right jsonValue <- maybeJson
Object jsonObject <- jsonValue
Just (String val) <- HashMap.lookup "key123" jsonObject
return val
case toPrint of
Just a -> Data.Text.IO.putStrLn a
_ -> error "Unexpected JSON"
And it gave me 3 errors:
src/Main.hs:86:19:
Couldn't match expected type `Value'
with actual type `Either t0 (Either String Value)'
In the pattern: Right jsonValue
In a stmt of a 'do' block: Right jsonValue <- maybeJson
src/Main.hs:88:19:
Couldn't match expected type `Value' with actual type `Maybe Value'
In the pattern: Just (String val)
In a stmt of a 'do' block:
Just (String val) <- HashMap.lookup "key123" jsonObject
src/Main.hs:88:40:
Couldn't match type `Maybe' with `Either String'
Expected type: Either String Value
Actual type: Maybe Value
Even when I replace
Just (String val) <- HashMap.lookup "key123" jsonObject
with
String val <- HashMap.lookup "key123" jsonObject
I'm getting another similar error about Either:
Couldn't match type `Maybe' with `Either String'
Expected type: Either String Value
Actual type: Maybe Value
In the return type of a call of `HashMap.lookup'
In a stmt of a 'do' block:
String val <- HashMap.lookup "key123" jsonObject
How do I fix those errors?
You can't easily simplify that into a single block of do-notation, because each case is matching over a different type. The first is unpacking an either, the second a Value and the third a Maybe. Do notation works by threading everything together through a single type, so it's not directly applicable here.
You could convert all the cases to use the same monad and then write it all out in a do-block. For example, you could have helper functions that do the second and third pattern match and produce an appropriate Either. However, this wouldn't be very different to what you have now!
In fact, if I was going for this approach, I'd just be content to extract the two inner matches into their own where variables and leave it at that. Trying to put the whole thing together into one monad just confuses the issue; it's just not the right abstraction here.
Instead, you can reach for a different sort of abstraction. In particular, consider using the lens library which has prisms for working with nested pattern matches like this. It even supports aeson nateively! Your desired function would look something like this:
decode :: String -> Maybe Value
decode json = json ^? key "key123"
You could also combine this with more specific prisms, like if you're expecting a string value:
decode :: String -> Maybe String
decode json = json ^? key "key123" . _String
This takes care of parsing the json, making sure that it's an object and getting whatever's at the specified key. The only problem is that it doesn't give you a useful error message about why it failed; unfortunately, I'm not good enough with lens to understand how to fix that (if it's possible at all).
So every line in a do expression for a Monad must return a value in that Monadic type. Monad is a typeclass here, not a type by itself. So putting everything in a do Monad is not really a sensible statement.
You can try your code with everything wrapped in a Maybe monad.
Assuming you've fetched your JSON value:
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
import Network.HTTP
import qualified Data.Map as M
import Control.Applicative
import qualified Data.HashMap.Strict as HM
--------------------------------------------------------------------
main = do
maybeJson <- return $ toJSON (M.fromList [("key123","value")] :: M.Map String String)
ioVal <- return $ do -- The Maybe monad do expression starts here
maybeJson <- Just maybeJson
jsonObject <- case maybeJson of
Object x -> Just x
_ -> Nothing
val <- HM.lookup "key123" jsonObject
return val
putStrLn $ show ioVal
Once we start working in the Maybe monad, every expression must return a Maybe Something value. The way the Maybe monad works is that anything that is a Just something comes out as a pure something that you can work with, but if you get a Nothing, the rest of the code will be skipped and you'll get a Nothing.
This property of falling through is unique to the Maybe monad. Different monads behave differently.
You should read up more about Monads and the IO monad here: http://www.haskell.org/haskellwiki/Introduction_to_IO
You should read more about monads and what they really help you do:
http://learnyouahaskell.com/a-fistful-of-monads
(You should work through the previous chapters and then get to this chapter. Once you do, you'll have a pretty solid understanding of what is happening).
I also think your HTTP request is screwed up. Here's an example of a POST request that you can use.
import qualified Network.HTTP as H
main = do
postData <- return $ H.urlEncodeVars [("someVariable","someValue")]
request <- return $ H.postRequestWithBody "http://www.google.com/recaptcha/api/verify" "application/x-www-form-urlencoded" postData
putStrLn $ show request
-- Make the request
(Right response) <- H.simpleHTTP request
-- Print status code
putStrLn $ show $ H.rspCode response
-- Print response
putSrLn $ show $ H.rspBody response
UPDATED:
Use the following to help you get a JSON value:
import qualified Data.ByteString.Lazy as LC
import qualified Data.ByteString.Char8 as C
import qualified Data.Aeson as DA
responseBody <- return $ H.rspBody response
responseJSON <- return (DA.decode (LC.fromChunks [C.pack responseBody]) :: Maybe DA.Value)
You'll have to make a request object to make a request. There are quite a few helpers. I meant the post request as the most generic case:
http://hackage.haskell.org/package/HTTP-4000.0.5/docs/Network-HTTP.html
Since you are in the IO monad, all the <- are going to assume that you are dealing with IO operations. When you write
do
Right jsonValue <- maybeJson
Object jsonObject <- jsonValue
you are saying that jsonValue must be an IO action just like maybeJson. But this is not the case! jsonValue is but a regular Either value. The silution here would ge to use a do-block let instead of a <-:
do
Right jsonValue <- maybeJson
let Object jsonObject = jsonValue
However, its important to note that in both versions of your code you are using an irrecoverable error to abort your program if the JSON parsing fails. If you want to be able to collect errors, the basic idea would be to convert your values to Either (and then use the monad instance for Either to avoid having lots of nested case expressions)

Aeson Example not working

I'm following the Aeson library documentation but their example doesn't seem to work for me:
Code:
{-# LANGUAGE OverloadedStrings #-}
import Data.Text
import Data.Aeson
import Control.Applicative ((<$>),(<*>))
import Control.Monad
instance FromJSON Person where
parseJSON (Object v) = Person <$>
v .: "name" <*>
v .: "age"
-- A non-Object value is of the wrong type, so fail.
parseJSON _ = mzero
data Person = Person
{ name :: Text
, age :: Int
} deriving Show
Error report:
ghci> decode "{\"name\":\"Joe\",\"age\":12}" :: Maybe Person
Couldn't match expected type `Data.ByteString.Lazy.Internal.ByteString'
with actual type `[Char]'
In the first argument of `decode', namely
`"{\"name\":\"Joe\",\"age\":12}"'
In the expression:
decode "{\"name\":\"Joe\",\"age\":12}" :: Maybe Person
In an equation for `a':
a = decode "{\"name\":\"Joe\",\"age\":12}" :: Maybe Person
Am i doing something wrong here ?
The problem is that decode expects a ByteString and you are passing a String.
Try this in ghci:
:m +Data.ByteString.Lazy.Char8
decode $ pack "{\"name\":\"Joe\",\"age\":12}" :: Maybe Person
In real code you shouldn't use the Char8 module as it just truncates Chars to 8 bits without taking any account of encoding. Generally you should aim to start out with a ByteString, e.g. by reading it from disk using the functions in Data.ByteString.Lazy.

Haskell Aeson JSON Library ByteString Issue

I'm having trouble finding a function or workaround to convert a String to Data.ByteString.Lazy.Internal.ByteString
One of the functions in the Aeson Json library is decode and has the following description:
decode :: FromJSON a => bytestring-0.10.0.2:Data.ByteString.Lazy.Internal.ByteString -> Maybe a
I've tried using the pack function in Data.ByteString.Lazy.Char8 but that returns a different ByteString. Any one know how this can be fixed?
The following is the example I'm working on:
import Data.Aeson
import Data.Text
import Control.Applicative
import Control.Monad (mzero)
import qualified Data.ByteString.Lazy.Internal as BLI
import qualified Data.ByteString.Lazy.Char8 as BSL
data Person = Person
{ name :: Text
, age :: Int
} deriving Show
instance FromJSON Person where
parseJSON (Object v) = Person <$>
v .: (pack "name") <*>
v .: (pack "age")
parseJSON _ = mzero
I tried using decode (BSL.pack "{\"name\":\"Joe\",\"age\":12}") :: Maybe Person
and got the following error message:
Couldn't match expected type `bytestring-0.10.0.2:Data.ByteString.Lazy.Internal.ByteString'
with actual type `BSL.ByteString'
In the return type of a call of `BSL.pack'
In the first argument of `decode', namely
`(BSL.pack "{\"name\":\"Joe\",\"age\":12}")'
In the expression:
decode (BSL.pack "{\"name\":\"Joe\",\"age\":12}") :: Maybe Person
Help!
You need to convert Char to Word8 using c2w (in Data.ByteString.Internal)
Data.ByteString.Lazy.pack $ map c2w "abcd"
I wrote out the fully qualified name for pack also to guarantee using the correct one, but you can clean this up in the imports section. When I run
> :t Data.ByteString.Lazy.pack $ map c2w "abcd"
I get ":: Data.ByteString.Lazy.Internal.ByteString"
Remember that Data.ByteString.Lazy represents strings of number values (you can't even run its pack on strings, you need to supply an array of numbers "pack [1, 2, 3, 4]"), so you might actually want to use the char equivalent Data.ByteString.Lazy.Char8.
You can also use for convenience fromString from Data.ByteString.Lazy.UTF8 from utf8-string.
It's a module of functions for the same ByteString type as aeson uses. It relyies on UTF8 as the encoding used in the buffers.

Resources