Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell - generating JSON with aeson gives incorrect order of fields

I am trying to encode a data type into JSON:

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

import Data.Aeson

data Trend = Trend
            { period :: String
            , africa :: String
            , americas :: String
            , asia :: String
            } deriving Show

instance ToJSON Trend where
  toJSON Trend{..} = 
    object [ "Period"    .= period
           , "Africa"    .= africa
           , "Americas"  .= americas
           , "Asia"      .= asia
           ]

test = Trend {period = "2013", africa = "1", americas = "2", asia = "3"}

Which gives:

λ: encode test
λ: "{\"Asia\":\"3\",\"Period\":\"2013\",\"Africa\":\"1\",\"Americas\":\"2\"}"

I don't understand why the generated JSON does not have the fields in the same order as my data type.

I am expecting the output to be {period, africa, americas, asia} and I am getting {asia, period, africa, americas)

I understand that in passing information across, the order is not important but I am curious as to why this is happening.

like image 801
matt Avatar asked Jun 16 '16 17:06

matt


2 Answers

You can use toEncoding method which is available since aeson-0.10 (use aeson-0.11 though, if only possible). In that case you have more control over the generated structure:

instance ToJSON Trend where
  toJSON Trend{..} = 
    object [ "Period"    .= period
           , "Africa"    .= africa
           , "Americas"  .= americas
           , "Asia"      .= asia
           ]

  toEncoding Trend {..} =
    pairs $ "Period"   .= period
         <> "Africa"   .= africa
         <> "Americas" .= americas
         <> "Asia"     .= asia 

Or in case as simple is this, is worth using Generic derivation

instance ToJSON Trend where
    toJSON = genericToJSON defaultOptions { fieldLabelModifier = capitaliseFirst }
      where
        capitaliseFirst (x:xs) = toUpper x : xs
        capitaliseFirst []     = []
like image 116
phadej Avatar answered Nov 15 '22 03:11

phadej


The reason it is happening is because an Aeson Object is just a HashMap, and when aeson converts the HashMap to text it just serializes the key-value pairs in the order that the HashMap returns them - which probably has no relationship to the order in which the keys were inserted.

like image 37
ErikR Avatar answered Nov 15 '22 04:11

ErikR