I'm trying to serialize an object into a JSON string and write it to a file.
In python, I'd do something like:
>>> meowmers = {"name" : "meowmers", "age" : 1}
>>> import json
>>> with open("myfile.json","wb") as f
json.dump(meowmers, f)
$ cat myfile.json
{"age": 1, "name": "meowmers"}
I'm looking at this in Haskell
$ stack ghci
{-# LANGUAGE OverloadedStrings #-}
:set -XOverloadedStrings
import GHC.Generics
import Data.Aeson as A
import Data.Text.Lazy as T
import Data.Text.Lazy.IO as I
:{
data Cat = Cat {
name :: Text
, age :: Int
} deriving Show
:}
let meowmers = Cat {name = "meowmers", age = 1}
writeFile "myfile.json" (encode meowmers)
Oh no!
*A T I GHC.Generics> I.writeFile "myfile2.json" (encode meowmers)
<interactive>:34:29:
Couldn't match expected type ‘Text’
with actual type ‘bytestring-0.10.6.0:Data.ByteString.Lazy.Internal.ByteString’
In the second argument of ‘I.writeFile’, namely ‘(encode meowmers)’
In the expression: I.writeFile "myfile2.json" (encode meowmers)
Two questions:
So, to iron everything out (since most of the work has already been done). You actually have two problems:
ToJSON
declared for Cat
Here is a working example that relies on recent versions of aeson
and text
(for me that is aeson-1.0.0.0
and text-1.2.2.1
.
{-# LANGUAGE OverloadedStrings, DeriveGeneric, DeriveAnyClass #-}
import GHC.Generics
import Data.Text.Lazy (Text)
import Data.Text.Lazy.IO as I
import Data.Aeson.Text (encodeToLazyText)
import Data.Aeson (ToJSON)
data Cat = Cat { name :: Text, age :: Int } deriving (Show, Generic, ToJSON)
meowmers = Cat { name = "meowmers", age = 1 }
main = I.writeFile "myfile.json" (encodeToLazyText meowmers)
As you can probably tell from the imports, I rely on aeson
to convert between string types through encodeToLazyText
. That deals with problem number 1.
Then, I use the language extension DeriveGeneric
to get a Generic
instance for Cat
, and use that in conjunction with the extension DeriveAnyClass
to get an instance of ToJSON
for Cat
. The magic of that instance is again part of aeson
.
Running this, I get a new file myfile.json
that contains {"age":1,"name":"meowmers"}
in it.
You can encode JSON to a lazy Text
value directly using Data.Aeson.Text.encodeToLazyText.
{-# LANGUAGE DeriveGeneric #-}
import Data.Aeson.Text (encodeToLazyText)
...
I.writeFile "myfile.json" (encodeToLazyText meowmers)
A bytestring
is a type for binary data—not necessarily text. To represent textual data in a bytestring, you need to encode it with some encoding like UTF-8. Once you have a bytestring (encoded with UTF-8 or whatever format makes sense), you can write it to a file using Data.ByteString
functions:
import qualified Data.ByteString.Lazy as BS
BS.writeFile "myfile.json" (encode meowmers)
To make this work you need to give your Cat
type a ToJSON
instance that specifies how to encode it in JSON. You can do this automatically with the DeriveGeneric
extension:
data Cat = Cat { ... } deriving (Show, Generic)
instance ToJSON Cat
You can also do this manually if you need finer control over what the resulting JSON looks like.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With