在 Aeson 中为 JSON 日期数据编写自定义实例
Writing custom instances for JSON date data in Aeson
我有 JSON 日期数据,格式如下:
{"date": "2015-04-12"}
和相应的 haskell 类型:
data Date = Date {
year :: Int
, month :: Int
, day :: Int
}
如何编写自定义 FromJSON
和 ToJSON
函数
Aeson图书馆?
由于格式原因,无法派生实例。
您已转换 y/m/d to/from 字符串
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-tabs #-}
import Control.Monad
import Data.Aeson
import qualified Data.Text as T
import Text.Read (readMaybe)
-- import qualified Data.Attoparsec.Text as A
data Date = Date Int Int Int deriving (Read, Show)
instance ToJSON Date where
toJSON (Date y m d) = toJSON $ object [
"date" .= T.pack (str 4 y ++ "-" ++ str 2 m ++ "-" ++ str 2 d)]
where
str n = pad . show where
pad s = replicate (n - length s) '0' ++ s
instance FromJSON Date where
parseJSON = withObject "date" $ \v -> do
str <- v .: "date"
let
ps@(~[y, m, d]) = T.split (== '-') str
guard (length ps == 3)
Date <$> readNum y <*> readNum m <*> readNum d
where
readNum = maybe (fail "not num") return . readMaybe . T.unpack
-- -- or with attoparsec
-- parseJSON = withObject "date" $ \v -> do
-- str <- v .: "date"
-- [y, m, d] <- either fail return $
-- A.parseOnly (A.decimal `A.sepBy` A.char '-') str
-- return $ Date y m d
为什么要重新发明轮子? time
package - it is called Day
中的 Date
有一个半标准表示。它变得更好:同一个包不仅为您提供了从您拥有的格式解析 Day
的实用程序,这些实用程序甚至还导出到 aeson
。是的,aeson
中已经有 ToJSON
和 FromJSON
个实例用于 Day
:
ghci> :set -XOverloadedStrings
ghci> import Data.Time.Calendar
ghci> import Data.Aeson
ghci> fromJSON "2015-04-12" :: Result Day
Success 2015-04-12
ghci> toJSON (fromGregorian 2015 4 12)
String "2015-04-12"
如果您确实想要提取日、月和年,您可以随时使用toGregorian :: Day -> (Integer, Int, Int)
。坚持标准抽象可能是一个很好的长期选择。
我有 JSON 日期数据,格式如下:
{"date": "2015-04-12"}
和相应的 haskell 类型:
data Date = Date {
year :: Int
, month :: Int
, day :: Int
}
如何编写自定义 FromJSON
和 ToJSON
函数
Aeson图书馆?
由于格式原因,无法派生实例。
您已转换 y/m/d to/from 字符串
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-tabs #-}
import Control.Monad
import Data.Aeson
import qualified Data.Text as T
import Text.Read (readMaybe)
-- import qualified Data.Attoparsec.Text as A
data Date = Date Int Int Int deriving (Read, Show)
instance ToJSON Date where
toJSON (Date y m d) = toJSON $ object [
"date" .= T.pack (str 4 y ++ "-" ++ str 2 m ++ "-" ++ str 2 d)]
where
str n = pad . show where
pad s = replicate (n - length s) '0' ++ s
instance FromJSON Date where
parseJSON = withObject "date" $ \v -> do
str <- v .: "date"
let
ps@(~[y, m, d]) = T.split (== '-') str
guard (length ps == 3)
Date <$> readNum y <*> readNum m <*> readNum d
where
readNum = maybe (fail "not num") return . readMaybe . T.unpack
-- -- or with attoparsec
-- parseJSON = withObject "date" $ \v -> do
-- str <- v .: "date"
-- [y, m, d] <- either fail return $
-- A.parseOnly (A.decimal `A.sepBy` A.char '-') str
-- return $ Date y m d
为什么要重新发明轮子? time
package - it is called Day
中的 Date
有一个半标准表示。它变得更好:同一个包不仅为您提供了从您拥有的格式解析 Day
的实用程序,这些实用程序甚至还导出到 aeson
。是的,aeson
中已经有 ToJSON
和 FromJSON
个实例用于 Day
:
ghci> :set -XOverloadedStrings
ghci> import Data.Time.Calendar
ghci> import Data.Aeson
ghci> fromJSON "2015-04-12" :: Result Day
Success 2015-04-12
ghci> toJSON (fromGregorian 2015 4 12)
String "2015-04-12"
如果您确实想要提取日、月和年,您可以随时使用toGregorian :: Day -> (Integer, Int, Int)
。坚持标准抽象可能是一个很好的长期选择。