Разбор Haskell, Aeson и JSON в настраиваемый тип

Следуя предыдущему сообщению, я обнаружил, что я полностью застрял. Я пытаюсь разобрать структуру JSON на свой собственный тип, и я не только застрял в том, как разобрать массив, я даже не уверен, использую ли я библиотеку Aeson по назначению. Любая помощь будет принята с благодарностью.

Код:

data Exif = Exif [(T.Text, ExifValue)] deriving (Show)
data ExifValue = 
    ExifText T.Text | 
    ExifInt Integer | 
    ExifDouble Double | 
    ExifBool Bool | 
    ExifArray [ExifValue] 
    deriving (Show)

instance FromJSON ExifValue where
    parseJSON (Number (I n)) = return $ ExifInt n
    parseJSON (Number (D n)) = return $ ExifDouble n
    parseJSON (String s)     = return $ ExifText s
    parseJSON (Bool b)       = return $ ExifBool b
    -- parseJSON (Array a)      = ?????

instance FromJSON Exif where
    parseJSON (Object o) = do
        x <- sequence $ map f (M.assocs o)
        return $ Exif x
        where 
        f (t, x) = do
            y <- parseJSON x 
            return ((t, y) :: (T.Text, ExifValue))

parseExifFile = fmap parseExifData . B.readFile

parseExifData :: B.ByteString -> Data.Attoparsec.Result (Data.Aeson.Result [Exif])
parseExifData content = parse (fmap fromJSON json) content

Тестовый файл:

[{
  "SourceFile": "test.jpg",
  "ExifTool:ExifToolVersion": 8.61,
  "File:FileName": "test.jpg",
  "File:FileSize": 2174179,
  "File:FileModifyDate": "2011:07:27 16:53:49-07:00",
  "File:FilePermissions": 644,
  "File:FileType": "JPEG",
  "File:MIMEType": "image/jpeg",
  "File:ExifByteOrder": "MM",
  "File:CurrentIPTCDigest": "32d6a77098a73aa816f2570c9472735a",
  "File:ImageWidth": 2592,
  "File:ImageHeight": 1936,
  "File:EncodingProcess": 0,
  "File:BitsPerSample": 8,
  "File:ColorComponents": 3,
  "File:YCbCrSubSampling": "2 2",
  "XMP:Subject": ["alpha","beta","gamma"]
}]

person Snoqual    schedule 03.08.2011    source источник


Ответы (3)


Вы должны следовать за типом parseJSON немного по кроличьей тропе, но как только вы поймете, что представляет собой (Array a), все будет просто.

parseJSON имеет тип Value -> Parser a, поэтому (Array a) имеет тип Value. Один из вариантов типа Value - это Array Array, поэтому a в (Array a) должен иметь тип Array, который определяется как Vector Value. Value внутри этого Vector - это то, что вы хотите вызвать parseJSON, чтобы вернуть свой список, поэтому проверьте, что вы можете сделать с помощью _ 16_.

Самый простой способ - преобразовать a в список с Vector.toList, а затем использовать mapM для синтаксического анализа Values.

В качестве альтернативы вы можете избежать преобразования Vector в список, изменив свой вариант ExifArray на удержание Vector ExifValue, а затем используя Vector.mapM.

person acfoltzer    schedule 03.08.2011

Я не говорю по-английски, поэтому я не очень хорошо вас понимаю. Я думаю, вы хотите знать, как преобразовать json в рекурсивный тип данных, такой как ExifValue, который вы представили. Итак, я сделал простой пример, чтобы показать, как преобразовать json в рекурсивный тип данных.

{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString as B
import Data.Maybe
import Control.Monad
import Control.Applicative
import Data.Attoparsec
import Data.Attoparsec.Number
import Data.Aeson
import qualified Data.Vector as V

data Data = D1 Int | D2 [Data]
    deriving (Show)

instance FromJSON Data where
    parseJSON (Number (I n)) = return $ D1 $ fromInteger n
    parseJSON (Array a)    = D2 <$> mapM parseJSON (V.toList a)

main = do
    let v = fromJust $ maybeResult $ parse json "[1,2,3,[5,3,[6,3,5]]]"
    let v1 :: Data
        v1 = case fromJSON v of
                 Success a -> a
                 Error s   -> error s
    print v1
person yihuang    schedule 04.08.2011

Немного более новая сборка aeson библиотека (0.3.2.12) поддерживает автоматическое создание экземпляров JSON.

{-# LANGUAGE TemplateHaskell #-}

import Data.Aeson
import Data.Aeson.TH (deriveJSON)
import Data.Attoparsec
import qualified Data.ByteString as B
import qualified Data.Text as T

data Exif = Exif [(T.Text, ExifValue)] deriving (Show)
data ExifValue = 
    ExifText T.Text | 
    ExifInt Integer | 
    ExifDouble Double | 
    ExifBool Bool | 
    ExifArray [ExifValue] 
    deriving (Show)

deriveJSON id ''Exif
deriveJSON id ''ExifValue

parseExifFile = fmap parseExifData . B.readFile

parseExifData :: B.ByteString -> Data.Attoparsec.Result (Data.Aeson.Result [Exif])
parseExifData content = parse (fmap fromJSON json) content

Производит:

instance ToJSON Exif where
  { toJSON
      = \ value_a1Va
          -> case value_a1Va of { Exif arg1_a1Vb -> toJSON arg1_a1Vb } }
instance FromJSON Exif where
  { parseJSON
      = \ value_a1Vc
          -> case value_a1Vc of {
               arg_a1Vd -> (Exif Data.Functor.<$> parseJSON arg_a1Vd) } }

instance ToJSON ExifValue where
  { toJSON
      = \ value_a1Wd
          -> case value_a1Wd of {
               ExifText arg1_a1We
                 -> object [(T.pack "ExifText" .= toJSON arg1_a1We)]
               ExifInt arg1_a1Wf
                 -> object [(T.pack "ExifInt" .= toJSON arg1_a1Wf)]
               ExifDouble arg1_a1Wg
                 -> object [(T.pack "ExifDouble" .= toJSON arg1_a1Wg)]
               ExifBool arg1_a1Wh
                 -> object [(T.pack "ExifBool" .= toJSON arg1_a1Wh)]
               ExifArray arg1_a1Wi
                 -> object [(T.pack "ExifArray" .= toJSON arg1_a1Wi)] } }
instance FromJSON ExifValue where
  { parseJSON
      = \ value_a1Wj
          -> case value_a1Wj of {
               Object obj_a1Wk
                 -> case Data.Map.toList obj_a1Wk of {
                      [(conKey_a1Wl, conVal_a1Wm)]
                        -> case conKey_a1Wl of {
                             _ | (conKey_a1Wl == T.pack "ExifText")
                               -> case conVal_a1Wm of {
                                    arg_a1Wn
                                      -> (ExifText Data.Functor.<$> parseJSON arg_a1Wn) }
                               | (conKey_a1Wl == T.pack "ExifInt")
                               -> case conVal_a1Wm of {
                                    arg_a1Wo
                                      -> (ExifInt Data.Functor.<$> parseJSON arg_a1Wo) }
                               | (conKey_a1Wl == T.pack "ExifDouble")
                               -> case conVal_a1Wm of {
                                    arg_a1Wp
                                      -> (ExifDouble Data.Functor.<$> parseJSON arg_a1Wp) }
                               | (conKey_a1Wl == T.pack "ExifBool")
                               -> case conVal_a1Wm of {
                                    arg_a1Wq
                                      -> (ExifBool Data.Functor.<$> parseJSON arg_a1Wq) }
                               | (conKey_a1Wl == T.pack "ExifArray")
                               -> case conVal_a1Wm of {
                                    arg_a1Wr
                                      -> (ExifArray Data.Functor.<$> parseJSON arg_a1Wr) }
                               | otherwise -> Control.Monad.mzero }
                      _ -> Control.Monad.mzero }
               _ -> Control.Monad.mzero } }
person Nathan Howell    schedule 06.11.2011