2011-07-03 11 views
6

Impossibile trovare un buon esempio. Apprezzo qualsiasi aiuto. Il JSON è la seguente:Haskell Aeson: come convertire il valore in un tipo personalizzato?

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

Il codice che ho usato è la seguente:

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

Come faccio a definire & utilizzare il tipo FromJSON convertire da x in:

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

o una struttura di dati simile? Nota il [[]] - Mi aspetto che il JSON abbia più voci di primo livello.

risposta

15

Ecco una soluzione idiomatica:

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

Testing:

$ 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"}] 

In alternativa, ecco una slightly more ugly solution che ti dà il tipo di dati che avete richiesto ([[(Text,Text)]]).

+1

+1 per uno dei primi esempi completi di aeson che ho trovato! Grazie! – oliver

Problemi correlati