簡體   English   中英

Haskell:將可能是多種類型的對象解析為一種類型

[英]Haskell: Parsing an object that could be multiple types into one single type

我是一個使用 aeson 的 Haskell 初學者,通過解析一些數據文件來了解更多關於兩者的信息。

通常當有數據文件時,可能是.jsonlua表、 .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 的實例,以便這個想法能夠實現。

這是我上一個問題的簡短后續

好吧,我嘗試使用 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一個實例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM