jsonhaskellaeson

How to parse an array with Haskell Aeson


I have a JSON doc that looks like:

{ "series": [[1,2], [2,3], [3,4]] }

I'd like to parse this into a set of data types:

data Series = Series [DataPoint]
data DataPoint = DataPoint Int Int  -- x and y

I'm having lots of problems trying to write the FromJSON instance for DataPoint.

instance FromJSON DataPoint where
  parseJSON (Array a) = ???

I've tried using Lens to destruct the DataPoint record, but it doesn't compile:

case a ^.. values . _Integer of -}
  [x,y] -> DataPoint <$> x <*> y
  _     -> mzero

That fails with this error (the first two lines I get even absent the lens trickery, just trying to create a DataPoint <$> 1 <*> 2):

Couldn't match type ‘aeson-0.7.0.6:Data.Aeson.Types.Internal.Parser
                       Integer’
              with ‘Integer’
Expected type: (aeson-0.7.0.6:Data.Aeson.Types.Internal.Parser
                  Integer
                -> Const
                     (Data.Monoid.Endo
                        [aeson-0.7.0.6:Data.Aeson.Types.Internal.Parse
                     (aeson-0.7.0.6:Data.Aeson.Types.Internal.Parser I
               -> Value
               -> Const
                    (Data.Monoid.Endo
                       [aeson-0.7.0.6:Data.Aeson.Types.Internal.Parser
                    Value
  Actual type: (Integer
                -> Const
                     (Data.Monoid.Endo
                        [aeson-0.7.0.6:Data.Aeson.Types.Internal.Parse
                     Integer)
               -> Value
               -> Const
                    (Data.Monoid.Endo
                       [aeson-0.7.0.6:Data.Aeson.Types.Internal.Parser
                    Value
In the second argument of ‘(.)’, namely ‘_Integer’
In the second argument of ‘(^..)’, namely ‘values . _Integer’

Is there a better way to do this?

Does anybody have an example of parsing arrays of values into a more detailed structure?


Solution

  • Aeson has an instance for list, so I think it is not necessary to deal with vectors.

    {-# LANGUAGE LambdaCase #-}
    import Data.Aeson
    
    data Series = Series [DataPoint]
    data DataPoint = DataPoint Int Int
    
    instance FromJSON DataPoint where
      parseJSON jsn = do
        [x,y] <- parseJSON jsn
        return $ DataPoint x y
    
    instance FromJSON Series where
      parseJSON = \case
        Object o -> (o .: "series") >>= fmap Series . parseJSON
        x -> fail $ "unexpected json: " ++ show x