ホーム > Haskell >

Data.AesonでJSONを扱う

aesonのインストール手順

mkdir foo && cd foo
cabal sandbox init
cabal update
cabal install aeson

JSON文字列をaesonのJSON型(Value)に変換する

import Data.Aeson
import qualified Data.ByteString.Lazy as LBS
import Control.Applicative ((<$>))

main :: IO ()
main = do
  maybeValue <- decode <$> LBS.readFile "test.json"
  case (maybeValue :: Maybe Value) of
    Just value -> print value
    Nothing    -> putStrLn "fail to decode."

JSON文字列をユーザ定義のデータ型に変換する

JSON文字列をユーザ定義型に変換する場合、ユーザ定義型をFromJSON型クラスのインスタンスとし、parseJSON関数を定義する必要がある。 parseJSON関数の型は、Value -> Parser (ユーザ定義型) である。 Applicativeスタイルで変換関数を定義できる他、GHCの拡張機能(DeriveGeneric)を使ってインスタンス宣言を自動化することもできる。

Lazy ByteStringからユーザ定義型への変換は、decode関数、または、eitherDecode関数で行う。

-- -*- coding: utf-8 -*-
{-# LANGUAGE OverloadedStrings #-}

import Data.Aeson
import qualified Data.ByteString.Lazy as LBS
import Control.Applicative ((<$>), (<*>))
import Control.Monad (mzero)

data Foo = Foo
         { hoge :: Integer
         , fuga :: [String]
         } deriving (Show)

instance FromJSON Foo where
    parseJSON (Object v) = Foo
                           <$> v .: "hoge"
                           <*> v .: "fuga"

    -- トップレベルの値がオブジェクトではない場合は、型エラーのためmzeroを返す
    parseJSON _ = mzero

main :: IO ()
main = do
  maybeFoo <- decode <$> LBS.readFile "test.json"
  case (maybeFoo :: Maybe Foo) of
    Just foo -> print foo
    Nothing  -> putStrLn "fail to decode."

  eitherFoo <- eitherDecode <$> LBS.readFile "test.json"
  case (eitherFoo :: Either String Foo) of
    Right foo -> print foo
    Left  msg -> putStrLn msg

test.jsonの内容

{
  "hoge" : 1,
  "fuga" : ["fugaValue1", "fugaValue2"]
}

実行結果

> cabal exec runhaskell fromJson.hs
Foo {hoge = 1, fuga = ["fugaValue1","fugaValue2"]}
Foo {hoge = 1, fuga = ["fugaValue1","fugaValue2"]}

DeriveGenericを使用した場合

{-# LANGUAGE DeriveGeneric #-}

import GHC.Generics
import Data.Aeson
import qualified Data.ByteString.Lazy as LBS
import Control.Applicative ((<$>), (<*>))

data Foo = Foo
         { hoge :: Integer
         , fuga :: [String]
         } deriving (Show, Generic)

instance FromJSON Foo

main :: IO ()
main = do
  maybeFoo <- decode <$> LBS.readFile "test.json"
  case (maybeFoo :: Maybe Foo) of
    Just foo -> print foo
    Nothing  -> putStrLn "fail to decode."

ユーザ定義のデータ型をJSON文字列に変換する

ユーザ定義型をJSON文字列に変換する場合、ユーザ定義型をToJSONクラスのインスタンスにして、encode関数を使ってユーザ定義型をLazy ByteStringに変換する。

{-# LANGUAGE OverloadedStrings #-}

import Data.Aeson
import Data.ByteString.Lazy.Char8 as LBS

data Coord = Coord
         { x :: Double
         , y :: Double
         } deriving (Show)

instance ToJSON Coord where
    toJSON (Coord x y) = object ["x" .= x, "y" .= y]

main :: IO ()
main = do
  LBS.putStrLn . encode $ Coord 1.0 2.0

DeriveGeneric拡張を使って、ToJSONクラスのインスタンス宣言を自動化することもできる。

{-# LANGUAGE DeriveGeneric #-}

import GHC.Generics
import Data.Aeson
import Data.ByteString.Lazy.Char8 as LBS

data Coord = Coord
         { x :: Double
         , y :: Double
         } deriving (Show, Generic)

instance ToJSON Coord

main :: IO ()
main = do
  LBS.putStrLn . encode $ Coord 1.0 2.0