Haskell Aeson: How to convert Value into custom type?

别来无恙 提交于 2019-12-19 06:59:09

问题


Can't find a good example. Appreciate any help. The JSON is as follows:

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

The code I used is as follows:

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

How do I define & use the FromJSON type to convert from x into:

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

or similar data structure? Note the [[]] -- I'm expecting the JSON to have multiple top-level entries.


回答1:


Here's an idiomatic solution:

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

Alternatively, here's a slightly more ugly solution that gives you the data type you requested ([[(Text,Text)]]).



来源:https://stackoverflow.com/questions/6566149/haskell-aeson-how-to-convert-value-into-custom-type

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!