Aeson でオプション値を含む JSON を扱う.

JSON の要素が全部が必須であれば簡単だけれども、そうでない場合が往々にしてある。公式のドキュメントにはサンプルがなかったので、ここにメモしておく。

値なし

Person の middleName がオプション値。型は Maybe Text。パース時に (.:?) を使用する。middleName が存在しなかった場合は、当然 Nothing だ。

{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
import Control.Applicative
import Control.Monad
import Data.Text
import Data.ByteString.Lazy.Char8

data Person = Person
     { firstName  :: Text
     , middleName :: Maybe Text
     , lastName   :: Text
     } deriving Show

instance FromJSON Person where
    parseJSON (Object v) = Person <$>
                           v .:  "firstName"  <*>
                           v .:? "middleName" <*>
                           v .:  "lastName"
    parseJSON _          = mzero

instance ToJSON Person where
    toJSON (Person fname mname lname) = object [ "firstName"  .= fname
                                               , "middleName" .= mname
                                               , "lastName"   .= lname
                                               ]

decodeWithMiddleName :: Maybe Person
decodeWithMiddleName = decode "{\"firstName\":\"John\", \"middleName\":\"Lambda\", \"lastName\":\"Doe\"}"

decodeWithoutMiddleName :: Maybe Person
decodeWithoutMiddleName = decode "{\"firstName\":\"John\", \"lastName\":\"Doe\"}"

encodeWithMiddleName :: ByteString
encodeWithMiddleName = encode $ Person "John" (Just "Lambda") "Doe"

encodeWithoutMiddleName :: ByteString
encodeWithoutMiddleName = encode $ Person "John" Nothing "Doe"

実行結果。

*Main> encodeWithoutMiddleName
"{\"middleName\":null,\"firstName\":\"John\",\"lastName\":\"Doe\"}"
*Main> decodeWithMiddleName
Just (Person {firstName = "John", middleName = Just "Lambda", lastName = "Doe"})
*Main> decodeWithoutMiddleName
Just (Person {firstName = "John", middleName = Nothing, lastName = "Doe"})
*Main> encodeWithMiddleName
"{\"middleName\":\"Lambda\",\"firstName\":\"John\",\"lastName\":\"Doe\"}"
*Main> encodeWithoutMiddleName
"{\"middleName\":null,\"firstName\":\"John\",\"lastName\":\"Doe\"}"

デフォルト値

パース時に値がなかった場合、デフォルト値を設定したい場合は (.!=) を使う。

{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
import Control.Applicative
import Control.Monad
import Data.Text

data Person = Person
     { firstName  :: Text
     , middleName :: Text -- Maybe じゃない
     , lastName   :: Text
     } deriving Show

instance FromJSON Person where
    parseJSON (Object v) = Person <$>
                           v .:  "firstName"  <*>
                           v .:? "middleName" .!= "Haskell" <*> -- デフォルト値 "Haskell"
                           v .:  "lastName"
    parseJSON _          = mzero

decodeWithMiddleName :: Maybe Person
decodeWithMiddleName = decode "{\"firstName\":\"John\", \"middleName\":\"Lambda\", \"lastName\":\"Doe\"}"

decodeWithoutMiddleName :: Maybe Person
decodeWithoutMiddleName = decode "{\"firstName\":\"John\", \"lastName\":\"Doe\"}"

結果

*Main> decodeWithMiddleName
Just (Person {firstName = "John", middleName = "Lambda", lastName = "Doe"})
*Main> decodeWithoutMiddleName
Just (Person {firstName = "John", middleName = "Haskell", lastName = "Doe"})