如何简化Haskell中多字段映射单一类型的JSON解析?
问题描述
现有两个JSON结构:
{ "fieldName1": 5, "value1": "Hello" }
和
{ "fieldName2": 7, "value1": "Welcome" }
对应的Haskell类型定义:
data Greeting = Greeting { count :: Int, name :: Text } deriving (Generic, Show, Eq)
需要将JSON中的fieldName1或fieldName2映射到count字段,原实现通过逐个读取可选字段再判断,当可选字段增多时会变得复杂,寻求更简洁的解析方式。
简洁解决方案
方案1:利用Alternative组合子直接尝试候选字段
aeson的Parser类型实现了Alternative类型类,可用<|>依次尝试解析各个候选字段,只要其中一个成功就返回结果。这种方式代码最简洁,新增字段时只需追加<|> obj .: "fieldNameN"。
instance FromJSON Greeting where parseJSON = withObject "Greeting" $ \obj -> do count <- obj .: "fieldName1" <|> obj .: "fieldName2" name <- obj .: "value1" return Greeting {count, name}
如果需要严格限制只能存在一个候选字段(不能同时有fieldName1和fieldName2),可以先收集所有存在的候选值再判断:
import Data.Maybe (catMaybes) instance FromJSON Greeting where parseJSON = withObject "Greeting" $ \obj -> do countCandidates <- catMaybes <$> sequence [ obj .:? "fieldName1" , obj .:? "fieldName2" -- 新增候选字段直接添加到列表中 ] count <- case countCandidates of [c] -> return c [] -> fail "缺少count字段(需提供fieldName1或fieldName2)" _ -> fail "存在多个count字段(仅允许fieldName1或fieldName2中的一个)" name <- obj .: "value1" return Greeting {count, name}
方案2:封装候选字段解析逻辑
如果候选字段数量较多,可以封装一个辅助函数统一处理,后续维护只需修改候选字段列表:
-- 辅助函数:尝试从对象中解析多个候选字段中的一个 parseOneOf :: FromJSON a => Object -> [Text] -> Parser a parseOneOf obj = foldr (<|>) (fail "未找到匹配的候选字段") . map (obj .:)
然后在FromJSON实例中使用:
instance FromJSON Greeting where parseJSON = withObject "Greeting" $ \obj -> do count <- parseOneOf obj ["fieldName1", "fieldName2"] name <- obj .: "value1" return Greeting {count, name}
内容的提问来源于stack exchange,提问作者DarkHorse1997
相关产品推荐
相关产品推荐

