Haskell(Aeson):以惯用方式处理含星期的时间字段解析为UTCTime
解决Aeson解析带星期的时间字符串到UTCTime的问题
针对你的JSONL格式({"mood": "Good", "when": "2022-10-09 Sun 11:51"}),以下是几种Haskell惯用的处理方式,直接去掉时间字符串中的星期部分后解析为UTCTime:
方法一:自定义FromJSON实例(推荐)
直接在解析目标数据结构时,单独处理when字段的字符串内容,过滤掉星期部分再解析。这种方式针对性强,不会影响JSON中的其他字段。
首先定义数据结构和必要的导入:
import Relude import Data.Aeson import Data.Time.Clock (UTCTime) import Data.Time.Format (parseTimeM, defaultTimeLocale) data MoodEntry = MoodEntry { mood :: Text , when :: UTCTime } deriving (Show, Eq)
然后实现自定义的FromJSON实例,核心是先清理时间字符串:
-- 清理时间字符串:过滤掉三个字母的星期缩写 cleanTimeStr :: Text -> Text cleanTimeStr = unwords . filter (\s -> length s /= 3) . words instance FromJSON MoodEntry where parseJSON = withObject "MoodEntry" $ \obj -> MoodEntry <$> obj .: "mood" <*> (obj .: "when" >>= \timeText -> case parseTimeM True defaultTimeLocale "%Y-%m-%d %H:%M" (toString $ cleanTimeStr timeText) of Just utcTime -> pure utcTime Nothing -> fail $ "无效的时间格式: " <> toString timeText)
这样调用decodeFileStrict就能直接解析JSONL文件:
main :: IO () main = do result <- decodeFileStrict "your-file.jsonl" :: IO (Maybe MoodEntry) print result
方法二:预处理整个JSON ByteString
如果确定JSON中没有其他类似" Sun "的子串,可以先批量替换所有星期缩写,再解析整个JSON。这种方式适合结构简单的场景:
import Relude import Data.Aeson import Data.Time.Clock (UTCTime) import qualified Data.ByteString as BS import qualified Data.Text.Encoding as TE -- 替换所有星期缩写为空格 preprocessJSON :: BS.ByteString -> BS.ByteString preprocessJSON = TE.encodeUtf8 . replace " Sun " " " . replace " Mon " " " . replace " Tue " " " . replace " Wed " " " . replace " Thu " " " . replace " Fri " " " . replace " Sat " " " . TE.decodeUtf8 data MoodEntry = MoodEntry { mood :: Text , when :: UTCTime } deriving (Show, Eq, FromJSON) -- 这里用自动派生的FromJSON即可 main :: IO () main = do rawBS <- BS.readFile "your-file.jsonl" let result = decode (preprocessJSON rawBS) :: Maybe MoodEntry print result
方法三:用Lens修改JSON Value
如果需要对JSON做多处字段修改,可以用aeson-lens库先修改when字段的内容,再解析为目标类型:
首先添加依赖aeson-lens,然后编写代码:
import Relude import Data.Aeson import Data.Aeson.Lens import Control.Lens import Data.Time.Clock (UTCTime) import Data.Time.Format (parseTimeM, defaultTimeLocale) data MoodEntry = MoodEntry { mood :: Text , when :: UTCTime } deriving (Show, Eq, FromJSON) cleanTimeStr :: Text -> Text cleanTimeStr = unwords . filter (\s -> length s /= 3) . words -- 修改JSON中的when字段值 modifyTimeField :: Value -> Value modifyTimeField = _Object . at "when" . _Just . _String %~ cleanTimeStr parseModified :: BS.ByteString -> Maybe MoodEntry parseModified bs = decode bs >>= \jsonVal -> parseJSON (modifyTimeField jsonVal) main :: IO () main = do rawBS <- BS.readFile "your-file.jsonl" print $ parseModified rawBS
内容的提问来源于stack exchange,提问作者Steven Pinkerton
相关产品推荐
相关产品推荐

