4

Im struggling to parse the below JSON using the Aeson library.
Im only interested in getting file1 but I cant seem to manage it.
Does anyone have suggestions?

The JSON

{"files":[["file1.wav",["file2.jpg","file3.jpg"]]]}

My code

data File = File Text deriving (Show, Generic, ToJSON)

instance FromJSON File where
  parseJSON jsn = do
    arrays <- parseJSON jsn
    let x = arrays !! 0 !! 0
    return $ File x

Error message

"Error in $.files[0][1]: parsing Text failed, expected String, but encountered Array"
revilotom
  • 75
  • 5

1 Answers1

3

The problem is using parseJSON to parse jsn into a homogeneous list. But "file1.wav" is a string and ["file2.jpg", "file3.jpg"] is not a string.

A simple solution is to directly pattern-match on json, which is a Value which can contain a heterogeneous Array (in spite of its name, it's actually a synonym for a Vector from the vector library).

{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
import qualified Data.Vector as V
import Data.Text (Text)

newtype File = File Text deriving Show

instance FromJSON File where
  parseJSON json = do
    Array arr <- pure json
    Just (Array arr0) <- pure (arr V.!? 0)
    Just (String txt) <- pure (arr0 V.!? 0)
    pure (File txt)

main :: IO ()
main = print (decode' "[[\"file1\", [\"file2\", \"file3\"]]]" :: Maybe File)

((!?) is a safe indexing operator.)

Li-yao Xia
  • 31,896
  • 2
  • 33
  • 56
  • Thanks so much for your response! When trying to compile your code it complained about "Just" so I removed them both and then it seemed to work fine. – revilotom Apr 05 '20 at 07:22
  • Oh, that's right. I was looking for a total version of `head` but then I got distracted. I've just fixed it by using `(!?)`, but dropping `Just` as you said also works. – Li-yao Xia Apr 05 '20 at 17:52
  • Thanks I didn’t know about the safe indexing operator! – revilotom Apr 07 '20 at 14:02