簡體   English   中英

Data.Aeson中的模式匹配矢量值

[英]Pattern Match Vector Value in Data.Aeson

我正在使用Data.Aeson將JSON解析為我的自定義類型。 我嘗試在我的FromJSON實例中模式匹配Vector ValueArray ),但不知道我該怎么做。 JSON value鍵可以有一個值String的列表, String或列表的列表String

instance FromJSON Foo where
  parseJSON (Object o) =
    case lookup "value" o of
      Just (String s) -> pure $ SimpleText s
      Just foo@(Array (String s)) -> pure $ ListOfText $ V.toList <$> V.mapM (parseJSON :: Value -> Parser Text) foo
      Just foo@(Array (Array (String s))) -> pure $ ListOfListOfText $ V.toList <$> V.mapM (parseJSON :: Value -> Parser Text) $ V.toList <$> V.mapM (parseJSON :: Value -> [Parser Value]) foo

data Foo = SimpleText Text
         | ListOfText [Text]
         | ListOfListOfText [[Text]]
         deriving (Show, Eq, Ord)

我可以在Array上使用模式匹配來處理這種情況嗎? 或者我應該手動檢查每個Value的類型? 怎么做?

不,你不能按照你在這里嘗試的方式進行模式匹配。 JSON數組可以包含不同類型的值,並且您不能對列表中的所有值進行模式匹配,就像它們中的值一樣。

有幾種方法可以解決您的實際問題。 有一種簡單的方法 ,有一種明確的方式可以為您提供更好的錯誤消息。

簡單的方法

簡單的方法是使用已經存在Text[a] FromJSON實例的事實。 因此,您可以使用Alternative運算符來編寫您的實例,如下所示:

instance FromJSON Foo where
    parseJSON v =  (SimpleText <$> parseJSON v) 
               <|> (ListOfText <$> parseJSON v) 
               <|> (ListOfListOfText <$> parseJSON v)

這里的訣竅是Aeson首先嘗試解析Text值,然后如果失敗則會嘗試[Text] ,如果再次失敗則會嘗試[[Text]]

此解決方案的問題在於,如果您的JSON格式不正確,則錯誤消息可能沒有意義。 例如,如果你給它一個頂級Null值,你的錯誤將是它期望[[Text]] ,因為你總是得到鏈中最后一個值的錯誤。

明確的方式

要獲得更好的錯誤消息,您必須更加關注您期望的值。 如果結果是空數組,它應該是ListOfText還是ListOfListOfText 由於我們無法直接在Vector上進行模式匹配,因此我們可以將其轉換為列表和模式匹配:

instance FromJSON Foo where
    parseJSON v = case v of
        -- If its a string, we return the string as a SimpleText
        (String s) -> return $ SimpleText s

        -- If its an array, we turn the vector to a list so we can pattern match on it
        (Array a)  -> case V.toList a of

            -- If its a empty list, we return a empty ListOfText
            []             -> return $ ListOfText []

            -- If the first value is a string, we put it as the first element of our ListOfTexts and try to parse the rest.
            (String s: xs) -> ListOfText . (s:) <$> mapM parseJSON xs

            -- If the first value is an array, we try to parse it as [Text], then parse the rest.
            (Array a: xa)  -> ListOfListOfText <$> ((:) <$> parseJSON (Array a) <*> mapM parseJSON xa)

            -- If the first value is neither a string or array we return a error message.
            _              -> fail "Expected an Array or an Array of Arrays."

        -- If the top level value is not a string or array we return a error message.
        _ -> fail "Expected a String or Array"

暫無
暫無

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

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