使用Haskell Aeson为省略字段设置默认值失效问题
问题原因
你错误使用了FromJSON类型类中的omittedField方法——这个方法的作用是当整个类型的值被省略时(例如嵌套结构中某个可选的Person字段被省略)提供默认值,而非为单个缺失的字段填充默认值。因此当JSON中缺少name或age字段时,通用解析器仍然会报错。
解决方法
要为单个缺失字段设置默认值,需要在genericParseJSON的配置选项中通过fieldDefaults指定每个字段的默认值。以下是两种可行的实现方式:
方式一:直接指定字段默认值
无需额外依赖,直接在配置中为每个字段定义默认的JSON值:
import Data.Aeson import Data.Aeson.Types (Value(..)) import GHC.Generics data Person = Person { name :: String , age :: Int } deriving (Generic, Show) instance FromJSON Person where parseJSON = genericParseJSON defaultOptions { allowOmittedFields = True -- 允许字段被省略 , fieldDefaults = DefaultFieldUse $ \fieldName -> case fieldName of "name" -> Just $ String "Unnamed" -- name字段缺失时用"Unnamed" "age" -> Just $ Number (-1) -- age字段缺失时用-1 _ -> Nothing } main :: IO () main = do print (eitherDecode @Person "{}") print (eitherDecode @Person "{\"name\":\"Bob\"}") print (eitherDecode @Person "{\"name\":\"Bob\",\"age\":42}")
方式二:结合Default类型类(更优雅)
导入Data.Default包,为Person实现默认实例,再复用该实例的字段值作为默认值:
import Data.Aeson import Data.Default (Default(..)) import Data.Aeson.Types (toJSON) import GHC.Generics data Person = Person { name :: String , age :: Int } deriving (Generic, Show) -- 定义Person的默认实例 instance Default Person where def = Person "Unnamed" (-1) instance FromJSON Person where parseJSON = genericParseJSON defaultOptions { allowOmittedFields = True , fieldDefaults = DefaultFieldUse $ \fieldName -> case fieldName of "name" -> Just $ toJSON (def.name) "age" -> Just $ toJSON (def.age) _ -> Nothing } main :: IO () main = do print (eitherDecode @Person "{}") print (eitherDecode @Person "{\"name\":\"Bob\"}") print (eitherDecode @Person "{\"name\":\"Bob\",\"age\":42}")
运行结果
修正后的代码运行后会得到预期输出:
Right (Person {name = "Unnamed", age = -1}) Right (Person {name = "Bob", age = -1}) Right (Person {name = "Bob", age = 42})
内容的提问来源于stack exchange,提问作者Futarimiti
相关产品推荐
相关产品推荐

