You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何简化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 00:56:19