Разбор вложенного массива объектов с помощью Aeson - PullRequest
5 голосов
/ 06 января 2012

Я хочу проанализировать объект JSON и создать JSONEvent с данными name и args

Я использую Aeson, и сейчас я застрял при преобразовании "args":[{"a": "b"}] в[(String, String)].

Спасибо заранее!

{-# 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 ]

3 голосов
/ 07 января 2012

Вот немного более сложный пример, основанный на примере Эхирда. Обратите внимание, что явная типизация при вызовах parseJSON не нужна, но я считаю их полезными для целей документирования и отладки. Также я не уверен, что вы хотели, но с args с несколькими значениями я просто объединяю все args вместе, вот так:

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

Вот код:

{-# 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
3 голосов
/ 06 января 2012

Я не эксперт по эсонам, но если у вас есть Object o, то o - это просто HashMap Text Value; Вы можете использовать Data.HashMap.Lazy.toList для преобразования его в [(Text, Value)] и Data.Text.unpack для преобразования Text s в String s.

Итак, предположительно, вы могли бы написать:

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
...