Haskell中如何处理Jogo数据类型JSON解析字段类型不匹配问题
类型不匹配解析异常处理方案
你当前使用aeson默认的Generic派生FromJSON实例,对JSON字段类型有严格校验,一旦字段类型和定义不匹配就会触发全局解析失败返回Nothing,可通过以下方案处理:
方案1:自定义兼容多类型的FromJSON实例
如果确定数值字段只会偶尔被误存为字符串格式的数字(比如"5"),可以自定义解析逻辑同时兼容数字和字符串类型:
{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE OverloadedStrings #-} import GHC.Generics (Generic) import Data.Aeson (FromJSON, Value(..), withObject, (.:)) import Data.Aeson.Types (Parser) import Data.Text (unpack) import Text.Read (readMaybe) import Control.Applicative ((<|>)) data Jogo = JogoInstance { rodada, golsMandante, golsVisitante :: Int, timeMandante, timeVisitante :: String } deriving (Generic, Show) instance FromJSON Jogo where parseJSON = withObject "Jogo" $ \v -> JogoInstance <$> v .: "rodada" <*> parseFlexibleInt "golsMandante" v <*> parseFlexibleInt "golsVisitante" v <*> v .: "timeMandante" <*> v .: "timeVisitante" where parseFlexibleInt field obj = do val <- obj .: field case val of -- 兼容数字类型输入 Number n -> pure $ truncate n -- 兼容字符串格式的数字输入 String s -> case readMaybe (unpack s) of Just intVal -> pure intVal Nothing -> fail $ "字段" <> field <> "的值" <> unpack s <> "无法转为数字" _ -> fail $ "字段" <> field <> "必须为数字或数字格式字符串"
方案2:优化错误输出,快速定位问题
默认的decode函数只会返回Nothing没有错误信息,换成eitherDecode可以拿到具体的解析失败原因,方便排查数据错误:
import Data.Aeson (eitherDecode) import qualified Data.ByteString.Lazy as B lerJSON :: IO [Jogo] lerJSON = do input <- B.readFile "db/jogos.json" case eitherDecode input of -- 打印错误信息后返回空列表,也可根据需求抛出异常或写入日志 Left err -> do putStrLn $ "JSON解析失败:" <> err return [] Right jogos -> return jogos
方案3:单条数据错误不影响整体加载
如果希望个别无效数据被自动忽略,不要导致整个列表解析失败,可以先解析为JSON值数组,再逐条解析过滤:
import Data.Aeson (eitherDecode, encode, FromJSON, Value) import Control.Monad (unless) import qualified Data.ByteString.Lazy as B lerJSON :: IO [Jogo] lerJSON = do input <- B.readFile "db/jogos.json" case eitherDecode input of Left err -> do putStrLn $ "JSON结构解析失败:" <> err return [] Right (valueList :: [Value]) -> do let parseResults = map parseSingle valueList validJogos = [j | Right j <- parseResults] invalidErrs = [e | Left e <- parseResults] -- 打印无效数据数量和示例错误 unless (null invalidErrs) $ putStrLn $ "忽略了" <> show (length invalidErrs) <> "条无效数据,首个错误:" <> head invalidErrs return validJogos where parseSingle v = eitherDecode (encode v) :: Either String Jogo
内容的提问来源于stack exchange,提问作者Thiago Melo
相关产品推荐
相关产品推荐

