Haskell, Aeson - Is there a better way of getting the info I need from USDA database?

Viewed 103

I'm a Haskell beginner trying to learn JSON parsing by going through USDA database.

I want to get the value of "ndbno" key from this link https://api.nal.usda.gov/ndb/search/?format=json&q=potato+salad&sort=n&max=25&offset=0&api_key=DEMO_KEY

The JSON file from that link looks like this:

{
    "list": {
        "q": "potato salad",
        "sr": "1",
        "ds": "any",
        "start": 0,
        "end": 25,
        "total": 251,
        "group": "",
        "sort": "n",
        "item": [
            {
                "offset": 0,
                "group": "Branded Food Products Database",
                "name": "AHOLD, DEVILED EGG POTATO SALAD, UPC: 688267141584",
                "ndbno": "45044170",
                "ds": "LI",
                "manu": "Ahold USA, Inc."
            },
            {
                "offset": 1,
                "group": "Branded Food Products Database",
                "name": "AHOLD, REDSKIN POTATO SALAD, UPC: 688267141591",
                "ndbno": "45044169",
                "ds": "LI",
                "manu": "Ahold USA, Inc."
                },
...

"nbdno" key is 4th in "item"

{-# LANGUAGE DeriveGeneric #-}

module Lib
    ( someFunc
    ) where


import           Data.Aeson
import           Data.HashMap.Strict  as HS
import           Data.List            as L
import           Data.Maybe
import           Data.Text
import           Data.Vector          as V
import           GHC.Generics
import           Network.HTTP.Conduit
import           Text.Pretty.Simple


data Food = Name (String, Text)
          | Stuff (String, Int)
    deriving (Eq, Read, Show, Generic)



someFunc :: IO ()
someFunc = putStrLn "Under Consruction!"


apiKey = "https://api.nal.usda.gov/ndb/search/?format=json&q=chana+dal&sort=n&max=25&offset=0&api_key=jJN9dsKFe507qjLVqbecijtrYB4AiojXVIt1EaEZ"


instance FromJSON Food


unString x = case x of
  String x -> x

unObject x = case x of
  Object x -> x

unArray x = case x of
  Array x -> x


getParsed :: IO ()
getParsed = do
  x <- simpleHttp apiKey
  let y = (unObject . snd . L.head . HS.toList . fromJust) (decode x :: Maybe Object)
      z = (unArray . snd . L.last . L.init) (HS.toList y)
      a = V.map (unpack . unString . snd . L.head . L.drop 5 . HS.toList . unObject) z
      b = V.map (\x -> read x :: Int) a
  pPrint b

The way I implemented it is quite messy, but it gets the job done.

It's just that the code looks really bad and almost impossible to read. So I was wondering if there's a better way of doing that.

1 Answers

Define the types you'd like to deserialise into, for example:

{-# LANGUAGE DeriveGeneric #-}
module Q53251928 where

import Data.Aeson
import GHC.Generics

data Item = Item { ndbno :: String } deriving (Eq, Show, Generic)
instance FromJSON Item

data FoodStuff = FoodStuff { item :: [Item] } deriving (Eq, Show, Generic)
instance FromJSON FoodStuff

data RootJSON = RootJSON { list :: FoodStuff } deriving (Eq, Show, Generic)
instance FromJSON RootJSON

These types are the minimal types you'll need to access all the ndbno values. You can add other labels to the types to capture other data, e.g. group, start, end, etc.

I downloaded the file in the link and attempted to decode it from GHCi, which worked fine:

*Q53251928 Q53251928> decodeFileStrict "download.json" :: IO (Maybe RootJSON)
Just (RootJSON {list = FoodStuff {item = [Item {ndbno = "45044170"},
Item {ndbno = "45044169"},Item {ndbno = "45287938"},Item {ndbno = "45314045"},
Item {ndbno = "45036851"},Item {ndbno = "45036824"},Item {ndbno = "45198766"},
Item {ndbno = "45270526"},Item {ndbno = "45270516"},Item {ndbno = "45374956"},
Item {ndbno = "45215370"},Item {ndbno = "45225694"},Item {ndbno = "45225696"},
Item {ndbno = "45217133"},Item {ndbno = "45053881"},Item {ndbno = "45270521"},
Item {ndbno = "45226407"},Item {ndbno = "45218344"},Item {ndbno = "45225703"},
Item {ndbno = "45335760"},Item {ndbno = "45221588"},Item {ndbno = "45193027"},
Item {ndbno = "45194338"},Item {ndbno = "45194331"},Item {ndbno = "45256801"}]}})
Related