Haskell Aeson JSON Library ByteString Issue - haskell

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.

Related

Data.Aeson import Key

I have the below code for parsing Int from an object. But unable to find the correct import for Key.
parseInt :: Object -> Key -> Parser Int
parseInt v field =
asum
[ v .: field,
do
s <- v .: field
case readMaybe s :: Maybe Int of
Nothing -> fail "not a number"
Just x -> return x
]
I have tried
import Data.Aeson
and
import Data.Aeson.Types
But get the error
Not in scope: type constructor or class ‘Key’
A data constructor of that name is in scope; did you mean DataKinds?
What should be the correct import for Key in my function?
It looks like you're writing code targeting aeson 2.x, but that you have aeson 1.x installed. Either upgrade (recommended), or use Text in place of Key there.

Parsing iCalendar format

I need to parse iCalendar format, which is basically what is used by Google Calendar and almost all calendar apps.
I have found this package iCalendar Hackage
But I cannot figure out how to use the parseICalendar function in this package, if someone can tell me what I am doing wrong, it would be great.
Mainly I cannot figure out how to construct an argument for the Type DecodingFunctions
parseICalendar :: DecodingFunctions
-> FilePath -- ^ Used in error messages.
-> ByteString
-> Either String ([VCalendar], [String])
My effort:
module CalendarReader
( getCalendar
, getSummary
) where
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString.Lazy as B -- package "bytestring"
import qualified Text.ICalendar as ICal -- package "iCalendar"
import qualified Data.Map as Map -- package "containers"
import Network.HTTP.Simple -- package "http-conduit"
import qualified Time -- local module
import Constants
getCalendar :: IO B.ByteString
getCalendar = do
request <- parseRequest $ "GET" ++ calendarURL
response <- httpLBS request
return $ getResponseBody response
getSummary :: B.ByteString -> Time.DateTime -> Int -> String
getSummary cal dateTime dayOffset = summary
where
summary = "Event Summary"
((ICal.VCalendar { ICal.vcEvents = vcEvents' }), _) = ICal.parseICalendar ?missingArgument? logFile cal
DecodingFunctions is supposed to contains a function to convert your ByteString (binary array) to a Text (representing a string of unicode characters) and one to do the same to a case-insensitive representation (for comparison purpose I suppose). If your iCalendar is "normal" and is encoded in utf-8, you can simply use the Default instance of DecodingFunctions :
parseICalendar def logFile cal
(don't forget to import def from somewhere)
If your iCalendar is not in Utf-8, you'll have to use a decode... function from Data.Text.Lazy.Encoding and mk from Data.CaseInsensitive. For Utf16 you would have :
decodings = DecodingFunctions decodeUtf16LE (mk . decodeUtf16LE)
with the right imports.

Avoiding String round-trip in Aeson

I have the following code (which is supposed to parse a very trivial { "url": "http://some.url.here/" } hash):
import Control.Applicative
import qualified Data.ByteString as B
import Data.ByteString.Char8 (pack)
import Data.Aeson ()
import Data.Aeson.Types
newtype SetNextUrl = SetNextUrl B.ByteString
instance FromJSON SetNextUrl where
parseJSON (Object v) = SetNextUrl <$>
(pack <$> v .: "url" )
Now notice that I'm hinting that "url" is of type String by using pack... This of course will cause some conversion overhead: from the input ByteString to a [Char] and back....
Question: How can I ask Aeson to interpret the "url" field as a ByteString?
aeson uses Text internally for string values, so if you use Data.Text.Encoding.encodeUtf8 you won't have the Text -> String -> ByteString conversion, it'll just go straight from Text -> ByteString (which iirc is fairly cheap)

Couldn't match expected type 'Data.ByteString.Lazy.Internal.ByteString' with actual type '[Char]'

I'm trying to get a simple Json parser up and running in my Haskell code, I came across Data.Aeson which seemed like a viable solution to my problem
I followed the example code on the page, and with some minor modifications, here's what I got:
{-#LANGUAGE OverloadedStrings #-}
import Data.Aeson
import Data.Text
import Control.Applicative
import Control.Monad
data Person =
Person { firstName :: Text
, lastName :: Text
, age :: Int
} deriving Show
instance FromJSON Person where
parseJSON (Object v) =
Person <$> v .: "f_name"
<*> v .: "l_name"
<*> v .: "age"
parseJSON _ = mzero
Running the following in GHCi causes the nasty message in the title to appear:
decode "{\"f_name\":\"Haskell\", \"l_name\":\"Curry\",\"age\":114}" :: Maybe Person
So, does anyone here have an idea what went wrong? I followed the example code almost exactly as it was written, so why is it that it fails?
Before calling decode in ghci, you need to do :set -XOverloadedStrings, so the string literal is treated as a ByteString instead of a String. The pragma in the module only applies to the code in the module, not to what you do in ghci.

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.

Resources