Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Aeson encoding of Data.Map.Strict.Map with custom key type results in array of arrays instead of object

I am having trouble getting Aeson to spit out objects when I use custom types as keys. Let me demonstrate:

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-}

import Data.Aeson

import qualified Data.Map.Strict as M
import qualified Data.ByteString.Lazy.Char8 as B

import GHC.Generics

data LOL = A | B | C deriving (Eq, Ord, Generic, ToJSONKey, ToJSON)
main = do
    B.putStrLn $ encode $ M.fromList [(A,"b")]
    B.putStrLn $ encode $ M.fromList [("A","b")]

In one case, I get an array of arrays, while in the other it's a regular object:

$ ./tojsonkey 
[["A","b"]]
{"A":"b"}

Any ideas?

like image 330
Sergio Losilla Avatar asked Oct 28 '25 05:10

Sergio Losilla


1 Answers

Take a look at the docs for ToJSONKey. Basically, the toJSONKey :: ToJSONKeyFunction a method handles two cases:

  1. when you can turn the key directly into something text like
  2. when the best you can do is turn the key into some general JSON

For the first of these, aeson will use a proper JSON object. For the latter, it falls back to nested arrays.

So why is it choosing option two in your case? Because you are deriving ToJSONKey and the default implementation chooses the second more general option. You can work around this problem by manually implementing ToJSONKey LOL:

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-}

import Data.Aeson
import Data.Aeson.Types

import qualified Data.Text as T
import qualified Data.Map.Strict as M
import qualified Data.ByteString.Lazy.Char8 as B

import GHC.Generics

data LOL = A | B | C deriving (Eq, Ord, Show, Generic, ToJSON)
instance ToJSONKey LOL where
  toJSONKey = toJSONKeyText (T.pack . show)

main = do
    B.putStrLn $ encode $ M.fromList [(A,"b")]
    B.putStrLn $ encode $ M.fromList [("A","b")]

That should give you

$ ./tojsonkey 
{"A":"b"}
{"A":"b"}
like image 66
Alec Avatar answered Oct 30 '25 22:10

Alec