5

I want to parse a JSON object and create a JSONEvent with the given name and args

I'm using Aeson, and right now I'm stucked on converting "args":[{"a": "b"}] to a [(String, String)].

Thank's in advance!

{-# LANGUAGE OverloadedStrings #-}

import Control.Applicative
import Data.Aeson

data JSONEvent = JSONEvent [(String, String)] (Maybe String) deriving Show

instance FromJSON JSONEvent where
  parseJSON j = do
    o <- parseJSON j
    name <- o .:? "name"
    args <- o .:? "args" .!= []
    return $ JSONEvent args name

let decodedEvent = decode "{\"name\":\"edwald\",\"args\":[{\"a\": \"b\"}]}" :: Maybe JSONEvent

2 Answers 2

3

Here's a bit more elaborate example based on ehird's example. Note that the explicit typing on calls to parseJSON is unnecessary but I find them useful for documentation and debugging purposes. Also I'm not sure what you intended, but with args with multiple values I simply concatenate all the args together like so:

*Main> decodedEvent2
Just (JSONEvent [("a","b"),("c","d")] (Just "edwald"))
*Main> decodedEvent3
Just (JSONEvent [("a","b"),("c","d")] (Just "edwald"))

Here's the code:

{-# LANGUAGE OverloadedStrings #-}

import Control.Applicative
import qualified Data.Text as T
import qualified Data.Foldable as F
import qualified Data.HashMap.Lazy as HM
import qualified Data.Vector as V
import Data.Aeson

import qualified Data.Attoparsec as P
import Data.Aeson.Types (Parser)
import qualified Data.Aeson.Types as DAT
import qualified Data.String as S

data JSONEvent = JSONEvent [(String, String)] (Maybe String) deriving Show

instance FromJSON JSONEvent where
  parseJSON = parseJSONEvent

decodedEvent = decode "{\"name\":\"edwald\",\"args\":[{\"a\": \"b\"}]}" :: Maybe JSONEvent
decodedEvent2 = decode "{\"name\":\"edwald\",\"args\":[{\"a\": \"b\"}, {\"c\": \"d\"}]}" :: Maybe JSONEvent
decodedEvent3 = decode "{\"name\":\"edwald\",\"args\":[{\"a\": \"b\", \"c\": \"d\"}]}" :: Maybe JSONEvent

emptyAesonArray :: Value
emptyAesonArray = Array $ V.fromList []

parseJSONEvent :: Value -> Parser JSONEvent
parseJSONEvent v =
  case v of
    Object o -> do
      name <- o .:? "name"
      argsJSON <- o .:? "args" .!= emptyAesonArray
      case argsJSON of
        Array m -> do
          parsedList <- V.toList <$> V.mapM (parseJSON :: Value -> Parser (HM.HashMap T.Text Value)) m
          let parsedCatList = concatMap HM.toList parsedList
          args <- mapM (\(key, value) -> (,) <$> (return (T.unpack key)) <*> (parseJSON :: Value -> Parser String) value) parsedCatList
          return $ JSONEvent args name
        _ -> fail ((show argsJSON) ++ " is not an Array.")
    _ -> fail ((show v) ++ " is not an Object.")

-- Useful for debugging aeson parsers
decodeWith :: (Value -> Parser b) -> String -> Either String b
decodeWith p s = do
  value <- P.eitherResult $ (P.parse json . S.fromString) s
  DAT.parseEither p value
Sign up to request clarification or add additional context in comments.

Comments

3

I'm not an aeson expert, but if you have Object o, then o is simply a HashMap Text Value; you could use Data.HashMap.Lazy.toList to convert it into [(Text, Value)], and Data.Text.unpack to convert the Texts into Strings.

So, presumably you could write:

import Control.Arrow
import Control.Applicative
import qualified Data.Text as T
import qualified Data.Foldable as F
import qualified Data.HashMap.Lazy as HM
import Data.Aeson

instance FromJSON JSONEvent where
  parseJSON j = do
    o <- parseJSON j
    name <- o .:? "name"
    Object m <- o .:? "args" .!= []
    args <- map (first T.unpack) . HM.toList <$> F.mapM parseJSON m
    return $ JSONEvent args name

2 Comments

I don' really get what's going on. I'm quite new at Haskell.. This is what I get:
@masylum: Ah, I misread your code slightly. Do you want the parsing to fail if, e.g. you have "args": [{"a":"b","c":d"}], i.e. an object with more than one key in the list? As for the "not in scope" error, you forgot the import Control.Arrow.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.