Dar*_*997 2 parsing json haskell aeson
假设我有以下 JSON 值。
{
"fieldName1": 5,
"value1": "Hello"
}
Run Code Online (Sandbox Code Playgroud)
和
{
"fieldName2": 7,
"value1": "Welcome"
}
Run Code Online (Sandbox Code Playgroud)
我在 Haskell 中有以下类型。
data Greeting
= Greeting
{
count :: Int,
name :: Text
}
deriving (Generic, Show, Eq)
Run Code Online (Sandbox Code Playgroud)
如何将此 JSON 解析为 Haskell,其中fieldName1或fieldName2值应解析为count值?
我尝试通过执行如下所示的操作来解决此问题。
instance FromJSON Greeting where
parseJSON = withObject "Greeting" $ \obj -> do
count1 <- obj .:? "fieldName1"
count2 <- obj .:? "fieldName2"
name <- obj .: "value1"
count <-
case (count1, count2) of
(Just count, Nothing) -> return count
(Nothing, Just count) -> return count
_ -> fail $ Text.unpack "Field missing"
return Greeting {count = count, name = name}
Run Code Online (Sandbox Code Playgroud)
它可以工作,但非常麻烦,如果有两个以上的替代值,它就会变得更加复杂。有什么办法可以更简单地解决这个问题吗?
Parser运行的 monad本身parseJSON就是一个,因此您可以在解析器定义中Alternative使用交替运算符:(<|>)
instance FromJSON Greeting where
parseJSON = withObject "Greeting" $ \o -> do
Greeting <$> (o .: "fieldName1" <|> o .: "fieldName2"
<|> fail "no count field")
<*> o .: "value1"
Run Code Online (Sandbox Code Playgroud)
如果存在多个“count”字段,则将采用第一个解析的字段。
如果您想以更编程的方式处理字段名称(例如,如果您想接受名称以前缀开头的单个字段,"field"同时拒绝具有多个匹配字段的情况),请注意,o可以KeyMap使用Data.Aeson.KeyMap和Data.Aeson.Key模块:
import Data.Aeson
import qualified Data.Aeson.Key as Key
import qualified Data.Aeson.KeyMap as KeyMap
import Data.List (isPrefixOf)
instance FromJSON Greeting where
parseJSON = withObject "Greeting" $ \o -> do
-- get all values for "field*" keys
let fields = [v | (k, v) <- KeyMap.toList o
, "field" `isPrefixOf` Key.toString k]
case fields of
[v] -> Greeting <$> parseJSON v <*> o .: "value1"
[] -> fail "no \"field*\" fields"
_ -> fail "multiple \"field*\" fields"
Run Code Online (Sandbox Code Playgroud)