Write json to a file in Haskell (with text, not [Char])

I am trying to serialize an object into a JSON string and write it to a file.

In python, I would 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 look at it 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!

 *ATI 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:

  • This seems to be a byte string. How can I work with this?
  • If this is not what I want to do, is there a Haskell json serialization solution using Text, not String, which is still pretty simple?
+5
source share
3 answers

So, to make out everything (since most of the work has already been completed). You actually have two problems:

  • You mix string types
  • You do not have an instance of ToJSON declared for Cat

Here is a working example based on the latest versions of aeson and text (for me it 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 import, I rely on aeson to convert between string types via encodeToLazyText . This concerns issue number 1.

Then I use the DeriveGeneric language DeriveGeneric to get a Generic instance for Cat , and use it in conjunction with the DeriveAnyClass extension to get a ToJSON instance for Cat . The magic of this instance is again part of aeson .

By running this, I get a new myfile.json file containing {"age":1,"name":"meowmers"} in it.

+6
source

You can encode JSON to the lazy value of Text using Data.Aeson.Text.encodeToLazyText .

 {-# LANGUAGE DeriveGeneric #-} import Data.Aeson.Text (encodeToLazyText) ... I.writeFile "myfile.json" (encodeToLazyText meowmers) 

A bytestring is a type of binary data, not text. To represent text data in a byte table, you need to encode it using some encoding such as UTF-8. After you have a byte sequence (encoded using UTF-8 or any other format), you can write it to a file using the Data.ByteString functions:

 import qualified Data.ByteString.Lazy as BS BS.writeFile "myfile.json" (encode meowmers) 

To do this, you need to provide a Cat instance of type ToJSON that tells 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.

+7
source

Moving all comments and answers to one (take note of other people, please accept one of their answers).

  • Print Generic and ToJSON (or manually write an instance of ToJSON if you want). This required a few more LANGUAGE pragmas.
  • Use either a newer version of text , and encodeToLazyText OR use Data.ByteString.Lazy.writeFile to write a byte string.

 {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveAnyClass #-} import GHC.Generics import Data.Aeson (encode,ToJSON(..)) import Data.Text.Lazy (Text) import qualified Data.ByteString.Lazy as BS data Cat = Cat { name :: Text , age :: Int } deriving (Show,Generic,ToJSON) main = do let meowmers = Cat {name = "meowmers", age = 1} BS.writeFile "myfile.json" (encode meowmers) 

Result:

 tommd@HalfAndHalf /tmp% runhaskell so.hs tommd@HalfAndHalf /tmp% cat myfile.json {"age":1,"name":"meowmers"} 
+3
source

All Articles