rcreswick
rcreswick

Reputation: 16833

Serialize instance for Data.Text?

I need to serialize a data type to disk that uses Data.Text, here's an example:

{-# LANGUAGE DeriveGeneric #-}
import Data.Serialize (Serialize)
import Data.Text (Text)
import GHC.Generics

data Foo = Foo Text deriving (Read, Show, Eq, Ord, Generic)
instance Serialize Foo
-- instance Serialize Text

As written, this generates the error:

No instance for (Serialize Text)
  arising from a use of `Data.Serialize.$gdmput'
Possible fix: add an instance declaration for (Serialize Text)
In the expression: (Data.Serialize.$gdmput)
In an equation for `put': put = (Data.Serialize.$gdmput)
In the instance declaration for `Serialize Foo'

If I uncomment the instance Serialize Text line, then this more cryptic error comes about:

No instance for (Data.Serialize.GSerialize (Rep Text))
  arising from a use of `Data.Serialize.$gdmput'
Possible fix:
  add an instance declaration for
  (Data.Serialize.GSerialize (Rep Text))
In the expression: (Data.Serialize.$gdmput)
In an equation for `put': put = (Data.Serialize.$gdmput)
In the instance declaration for `Serialize Text'

I could implement the Serialize instance manually, but this seems like a situation where orphaned instances would be a real problem, and furthermore, I don't think I know enough about Data.Text to serialize / deserialize it quickly and correctly.

Is there a standard solution to this problem? (I'm also not wedded to using cereal's Serialize instance, but I have been having some version issues relating to using the binary package; binary-0.5.1.1 doesn't seem to support Generics well, and I'd like to avoid writing boilerplate.)

Upvotes: 7

Views: 975

Answers (3)

Uli Köhler
Uli Köhler

Reputation: 13750

In order to address this issue, I just published cereal-text on Hackage.

The implementation is similar to rcreswick's answer, however cereal-text also provides an instance for lazy text.

Install the library using cabal install cereal-text and import the instances using:

import Data.Serialize.Text ()

Note that for some applications safecopy is not usable, because it depends on Template Haskell which is not available on all platforms.

Upvotes: 3

rcreswick
rcreswick

Reputation: 16833

Here's the instance I ended up using, based on Joachim Britner's advice:

instance Serialize Text where
  put txt = put $ encodeUtf8 txt
  get     = fmap decodeUtf8 get

As pointed out, you may need different encode / decode functions, but the structure should be the same.

Upvotes: 5

Joachim Breitner
Joachim Breitner

Reputation: 25782

I believe that the missing instance should be considered a feature: Data.Text talks about text, which should be kept distinct from its representation. So the canonical way to approach the problem is to encode the Data.Text to a Data.ByteString in the encoding you want it to be (or, better, some specification or metadata wants it to be), and then serialize the Data.ByteString.

The most common encoding functions can be found in Data.Text.Encoding.

Upvotes: 4

Related Questions