我是aeson的haskell初学者,通过解析一些数据文件来了解有关两者的更多信息。
通常,当有数据文件时,它可能是.json
,lua
表,.csv
格式或其他格式,而您想解析它们,则总是有可能出错。
例如,像这样的简单.json
文件
"root": {
"m1": {
"key1": "value1",
"key2": 2
},
"m2": {
"key1": 1
},
}
具有两个奇数:"m1"
有两个子项,一个在String
中有一个值,在Int
中有一个。 "m2"
仅具有一个子键,它与上面的子键具有相同的键,但是值具有不同的类型,即。 Int
。
如果是这样
"root": {
"m1": {
"key1": "value1",
"key2": 2
},
"m2": {
"key1": "value1",
"key2": 2
},
}
使用Aeson解析它的一种简单方法就是使用这些数据类型
data Root = Root { Map String Key
} deriving (Show, Generic)
data Key = Key { key1 :: String
, key2 :: Int
} deriving (Show, Generic)
如果缺少钥匙
"root": {
"m1": {
"key1": "value1",
"key2": 2
},
"m2": {
"key1": "value1"
},
}
这可以完成工作
data Root = Root { Map String Key
} deriving (Show, Generic)
data Key = Key { key1 :: String
, key2 :: Maybe Int
} deriving (Show, Generic)
但是,如果像第一个示例那样,键不仅不具有值,而且具有完全不同的键,该怎么办?
如果您只关心数字或字符串怎么办?有没有一种不脱离类型定义的解析方法?
通过一些快速搜索,我发现Alternative类仅用于解决此类问题,并且*>
,<>
,<|>
之类的运算符可以证明是有用的,但我不知道如何。
我知道我需要定义一个可以封装所有三种可能性的类型,如果我只想要文本或数字,例如
Data NeededVal = NoValue | TextValue | Needed Int
或
Data NeededVal = NoValue | NumericValue | Needed String
但是我不确定如何使它们成为Applicative&Alternative的实例,以使想法得以实现。
这是我先前的question
的简短跟进答案 0 :(得分:1)
好吧,我尝试使用JSON,如下所示:
"root": {
"m1": {
"key1": "value1",
"key2": 2
},
"m2": {
"key1": 1
},
}
并使用 Data.Aeson 将其解析为以下数据类型:
data Root = Root (Map String Key) deriving (Show)
data NeededVal = NoValue | NumericValue | Needed String deriving (Show)
data Key = Key { key1 :: NeededVal , key2 :: NeededVal } deriving (Show)
要处理NoValue
,我将替代项<|>
用作
instance FromJSON Key where
parseJSON = withObject "Key" $ \obj -> do
k1 <- obj .: (pack "key1") <|> pure NoValue
k2 <- obj .: (pack "key2") <|> pure NoValue
return(Key k1 k2)
要测试String
和numeric
类型,我将Value
构造函数用作:
instance FromJSON NeededVal where
parseJSON (String txt) = return $ Needed $ unpack txt
parseJSON (Number _) = return $ NumericValue
parseJSON _ = return NoValue
要跳过m1
和m2
对象并立即以以下方式读取keys
值:
import Data.Map as Map (Map, fromList)
import Data.HashMap.Strict as HM (toList, lookup)
import Data.Aeson.Types (Parser)
parseJSON = withObject "Root"
$ \rootObj-> case HM.lookup (pack "root") rootObj of
Nothing -> fail "no Root"
Just val -> withObject "Key List" mkRoot val
where mkRoot obj =
let (ks, vs) = unzip $ HM.toList obj
ks' = map unpack ks
in do vs' <- mapM parseJSON vs::Parser [Key]
return $ Root $ Map.fromList $ zip ks' vs'
和最终结果:
Right (Root (fromList [
("m1",Key {key1 = Needed "value1", key2 = NumericValue}),
("m2",Key {key1 = NumericValue, key2 = NoValue})]
))
旁注:
但我不确定如何将它们设置为 适用性和替代性,这样就可以解决这个想法。
否,无需将它们作为Applicative and Alternative
的实例,<|>
运算符应用于Parser
(在Data.Aeson.Types
中定义)不是用户定义的数据类型。 Parser
已经是Alternative
的实例。