使用Aeson解析JSON

时间:2017-02-03 19:47:44

标签: json haskell aeson

Cosider以下JSON结构:

{"k1":
  {"k2":
    [{"a": 3, "b": 4, "c": 2},
     {"a": 1, "b": 2, "c": 9}]},
 "irrelevant": "x"}

和Haskell数据类型:

data My = My Int Int

上面的JSON应该解析为My:[My]的List,而两个Int应该从JSON数组的“a”和“b”键中取出:

[My 3 4, My 1 2]

不可否认,我已经面临着最简单部分的麻烦。

以下是我开始使用Aeson的方式:

import           Data.Aeson
import qualified Data.ByteString.Lazy.Char8 as L8

sample :: L8.ByteString
sample = "{\"k1\":{\"k2\":[{\"a\": 3, \"b\": 4, \"c\": 2}, {\"a\": 1, \"b\": 2, \"c\": 9}]}, \"irrelevant\": \"x\"} "

在repl:

decode sample :: Maybe Object
Just (Object (fromList [("irreleva...

这可以正常工作,解析JSON。但是,下一步,将对象设置为键“k1”不起作用:

:t (fromJust $ (decode sample :: Maybe Object)) .: "k1"
...
  :: FromJSON a => aeson-0.11.2.1:Data.Aeson.Types.Internal.Parser a

我在这里收到Parser a类型,此时我需要/期望获得另一个ObjectMaybe Object

我是在正确的道路上吗?

2 个答案:

答案 0 :(得分:0)

我将从头开始然后回到你的问题。

使用课程解决

通常,您为每个JSON类型创建一个Haskell数据类型,并编写实现解析器的FromJSON类。你没有必要,但它确实减轻了心理负担,并与你在其他项目中观察到的内容一致。为此,我们只为您的元素制作一些My类型,为Mys制作这些元素的列表:

{-# LANGUAGE OverloadedStrings #-}
import           Data.Aeson
import qualified Data.ByteString.Lazy.Char8 as L8
import qualified Data.Vector as V

sample :: L8.ByteString
sample = "{\"k1\":{\"k2\":[{\"a\": 3, \"b\": 4, \"c\": 2}, {\"a\": 1, \"b\": 2, \"c\": 9}]}, \"irrelevant\": \"x\"} "

newtype Mys = Mys [My]
    deriving (Eq,Ord,Show)
data My = My Int Int
    deriving (Eq,Ord,Show)

好的,没问题。现在,我们可以从您的k1记录中提取abc对象的列表,并在这些对象上运行My解析器,以获得ab值:

instance FromJSON Mys where
  parseJSON (Object v) = do
   do k1Val <- v .: "k1"
      case k1Val of
        Object k1 ->
          do k2Val <- k1 .: "k2"
             Mys . V.toList <$> mapM parseJSON k2Val
        _         -> fail "k1 was not an Object"
  parseJSON o = fail $ "Invalid type for Mys: " ++ show o

也就是说,要解析Mys我们需要一个对象,该对象必须有一个k1条目,这是另一个对象。 k1必须有一个k2条目,我们可以将其Vector My分析为instance FromJSON My where parseJSON (Object v) = My <$> v .: "a" <*> v .: "b" parseJSON o = fail $ "Invalid type for My: " ++ show o

My

a数据只是将bInt字段解析为> decode sample :: Maybe Mys Just (Mys [My 3 4,My 1 2]) 。看哪:

:t (fromJust $ (decode sample :: Maybe Object)) .: "k1"

没有课程

您问过.:,这只是询问> :t (.:) (.:) :: FromJSON a => Object -> Text -> Parser a 类型的一种奇特方式:

Parser

正如你所说,所以你提供了一个对象和文本来获得Parser。我不建议再次使用decode monad - 您实际上只是将其用于case。总之,我说不,你没有走上幸福之路。

如果您未按设计使用API​​,则只需忘记组合器并直接使用数据类型。也就是说,Value类型的Object被破坏了很多。首先是k1,它是HashMap(只是一个Array)然后提取k2值,这是Vector(a a),最后对于Vector的每个元素你再次提取一个Object并在那里查找bMaybe个键。我打算写一下这个例子,但是如果你不至少允许自己$(document).ready(function(){ var bquestionname = $.trim($("#questionname").val()); alert(bquestionname); $('#google_login').oauthpopup({ path: rootUrl+'social/login.php?google&bquestionname='+bquestionname, width:650, height:350, }); }); monad那就非常难看。

答案 1 :(得分:0)

教程'Aeson: the tutorial' by Artyom可以帮助你,因为它帮助了我。

在它之后,我得到了以下代码。它扩展sample以允许处理不同的样本(一些具有各种缺陷)。 main期望将要处理的样本的标识作为已编译可执行文件的第一个参数提供。

从JSON结构的底部开始,向上工作,函数parseMy :: Value -> Parser My处理一个对象&#39; a&#39; &#39; B&#39;产生My的钥匙(如果成功的话);中间辅助函数parseMyList'处理此类对象的数组以生成My的列表;并parseMyList使用键&#39; k1&#39;处理一个对象,然后依次产生一个带有键&#39;&#39;的对象,以产生一个My的列表。

main中,parseparseMyList :: Value -> Parser [My]应用于decode的结果(如果成功)。

{-# LANGUAGE OverloadedStrings #-}

module Main (main) where

import           Data.Aeson ((.:), decode, Value)
import           Data.Aeson.Types (parse, Parser, withArray, withObject)
import qualified Data.ByteString.Lazy.Char8 as L8 (ByteString)
import qualified Data.Vector as V (toList)
import           System.Environment (getArgs)

data My = My Int Int deriving (Show)

sample :: String -> L8.ByteString
sample "1" = "{\"k1\":{\"k2\":[{\"a\": 3, \"b\": 4, \"c\": 2}, {\"a\": 1, \"b\": 2, \"c\": 9}]}, \"irrelevant\": \"x\"} "
sample "2" = "{\"k1\":{\"k2\":[{\"a\": 3, \"b\": 4, \"c\": 2}, {\"a\": 1, \"c\": 9}]}, \"irrelevant\": \"x\"} "
sample "3" = "{\"k1\":{\"k3\":[{\"a\": 3, \"b\": 4, \"c\": 2}, {\"a\": 1, \"b\": 2, \"c\": 9}]}, \"irrelevant\": \"x\"} "
sample "4" = "{\"k1\":{\"k2\":[{\"a\": 3, \"b\": 4, \"c\": 2}]}, \"irrelevant\": \"x\"} "
sample _   = "Error"

parseMy :: Value -> Parser My
parseMy = withObject "object" $ \o -> do
    a <- o .: "a"
    b <- o .: "b"
    return $ My a b

parseMyList' :: Value -> Parser [My]
parseMyList' = withArray "array" $ \arr ->
    mapM parseMy (V.toList arr)

parseMyList :: Value -> Parser [My]
parseMyList = withObject "object" $ \o -> do
    k1 <- o .: "k1"
    k2 <- k1 .: "k2"
    parseMyList' k2 

main :: IO ()
main = do
    args <- getArgs
    case args of
        []  -> fail "expected sample identity as the first argument"
        n:_ -> do
            putStrLn $ "Processing sample " ++ n
            case decode (sample n) of
                Just result -> print $ parse parseMyList result
                Nothing     -> fail "decoding failed"