1
votes

The newest version of Data.Aeson changed the way that ToJSON and FromJSON work for simple types like:

data Permission = Read | Write

It used to be that the generic call:

instance ToJSON Permission where 

...Would create JSON that looked like {"Read":[]} or {"Write":[]}.

But now it creates: {tag:"Read",contents:"[]"}

Which makes sense but breaks code I have written. I wrote a toJSON part by hand to give the correct looking stuff but writing the fromJSON is confusing me.

Any ideas?

Thanks

2
I'm a bit confused as to why you'd want to/fromJSON instances for this data type in the first place. This data type represents a constant more-so than an abstract type. I could see this being used as a part of a larger data structure that had a given permission, i.e. {..., "permission" : "read"}. Care to elaborate on how it is being used?fredugolon

2 Answers

2
votes

You could control how datatype with all nullary constructors is encoded using allNullaryToStringTag field on Data.Aeson.Options. Set it to True and it will be encoded simply as string.

import Data.Aeson.Types (Options (..), defaultOptions)

data Permission = Read | Write

$(deriveToJSON (defaultOptions {allNullaryToStringTag = True}) ''Permission)

Take a look at Options definition, it contains other handy fields.

1
votes

Since the value contained in the Object constructor for Data.Aeson.Value is just a strict HashMap, we can extract the keys from it and make a decision based on that. I tried this and it worked pretty well.

{-# LANGUAGE OverloadedStrings #-}
module StackOverflow where

import Data.Aeson
import Control.Monad
import Data.HashMap.Strict (keys)

data Permission = Read | Write

instance FromJSON Permission where
    parseJSON (Object v) =
        let ks = keys v
        in case ks of
            ["Read"] -> return Read
            ["Write"] -> return Write
            _ -> mzero
    parseJSON _ = mzero

You can test it with decode "{\"Read\": []}" :: Maybe Permission. The mzero in parseJSON ensures that if something else is passed in, it'll just return Nothing. Since you seem to want to only check if there is a single key matching one of your two permissions, this is pretty straightforward and will properly return Nothing on all other inputs.