0

我是一个通过 aeson 的 haskell 初学者,通过解析一些数据文件来了解更多关于这两者的知识。

通常当有一个数据文件时,可能是.json一个lua表格,.csv格式或其他,并且你想解析它们,总是有错误的机会。

例如,.json像这样的简单文件

"root": {
     "m1": {
      "key1": "value1",
      "key2": 2
       },
     "m2": {
       "key1": 1
       },
}

有两个奇怪的地方:"m1"有两个子键,一个有值 in 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 的实例,以便这个想法能够奏效。

这是我上一个问题的简短后续

4

1 回答 1

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,我使用 Alternative<|>作为

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)

为了测试Stringnumeric输入,我使用Value构造函数:

instance FromJSON NeededVal where
    parseJSON (String txt) = return $ Needed $ unpack txt
    parseJSON (Number _)   = return $ NumericValue
    parseJSON _            = return NoValue

跳过m1m2对象并立即读取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 & Alternative 的实例,以便这个想法能够奏效。

,不需要将它们作为 的实例Applicative and Alternative<|>运算符应用于Parser(定义在Data.Aeson.Types)而不是用户定义的数据类型。Parser已经是 的一个实例Alternative

于 2018-11-27T09:03:39.907 回答