2011-07-03 15 views
6

No se puede encontrar un buen ejemplo. Aprecia cualquier ayuda. El JSON es el siguiente:Haskell Aeson: ¿Cómo convertir el valor en un tipo personalizado?

[{ 
    "EXIF:Make": "Canon", 
    "EXIF:Model": "Canon PowerShot S95", 
    "EXIF:Orientation": "Horizontal (normal)", 
    "EXIF:XResolution": 180, 
    "EXIF:YResolution": 180, 
    "EXIF:ResolutionUnit": "inches" 
}] 

El código que utiliza es de la siguiente manera:

import Data.Aeson 
import Data.Attoparsec 
import Data.ByteString 
x <- fmap (parse json) (Data.ByteString.readFile "json.txt") 

¿Cómo definir & utilizar el tipo de FromJSON convertir de x en:

data Exif = Exif [[(String, String)]] 

o estructura de datos similar? Tenga en cuenta el [[]] - Estoy esperando que el JSON tenga múltiples entradas de nivel superior.

Respuesta

15

Aquí es una solución idiomática:

{-# LANGUAGE OverloadedStrings #-} 

module Main 
     where 

import Control.Applicative 
import Control.Monad 
import Data.Aeson 
import Data.Attoparsec 

import qualified Data.ByteString as B 
import qualified Data.Text as T 

data ExifEntry = ExifEntry { exifMake :: T.Text, 
          exifModel :: T.Text, 
          exifOrientation :: T.Text, 
          exifXResolution :: Int, 
          exifYResolution :: Int, 
          exifResolutionUnit :: T.Text 
          } deriving (Eq, Show) 


instance FromJSON ExifEntry 
    where 
    parseJSON (Object v) = ExifEntry <$> 
          v .: "EXIF:Make" <*> 
          v .: "EXIF:Model" <*> 
          v .: "EXIF:Orientation" <*> 
          v .: "EXIF:XResolution" <*> 
          v .: "EXIF:YResolution" <*> 
          v .: "EXIF:ResolutionUnit" 
    parseJSON _   = mzero 


parseAll :: B.ByteString -> [ExifEntry] 
parseAll s = case (parse (fromJSON <$> json) s) of 
    Done _ (Error err) -> error err 
    Done ss (Success e) -> e:(parseAll ss) 
    _     -> [] 

main :: IO() 
main = do s <- B.readFile "json.txt" 
      let p = parseAll s 
      print p 

Pruebas:

$ cat json.txt 
{ 
    "EXIF:Make": "Canon", 
    "EXIF:Model": "Canon PowerShot S95", 
    "EXIF:Orientation": "Horizontal (normal)", 
    "EXIF:XResolution": 180, 
    "EXIF:YResolution": 180, 
    "EXIF:ResolutionUnit": "inches" 
} 

{ 
    "EXIF:Make": "Canon", 
    "EXIF:Model": "Canon PowerShot S995", 
    "EXIF:Orientation": "Horizontal (normal)", 
    "EXIF:XResolution": 180, 
    "EXIF:YResolution": 180, 
    "EXIF:ResolutionUnit": "inches" 
} 
$ ./dist/build/test/test 
[ExifEntry {exifMake = "Canon", exifModel = "Canon PowerShot S95", exifOrientation = "Horizontal (normal)", exifXResolution = 180, exifYResolution = 180, exifResolutionUnit = "inches"},ExifEntry {exifMake = "Canon", exifModel = "Canon PowerShot S995", exifOrientation = "Horizontal (normal)", exifXResolution = 180, exifYResolution = 180, exifResolutionUnit = "inches"}] 

Alternativamente, aquí hay un slightly more ugly solution que le da el tipo de datos que ha solicitado ([[(Text,Text)]]).

+1

+1 para uno de los primeros ejemplos completos de aeson que encontré! ¡Gracias! – oliver

Cuestiones relacionadas