首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用Aeson解析JSON

用Aeson解析JSON
EN

Stack Overflow用户
提问于 2017-02-04 03:47:44
回答 2查看 1.2K关注 0票数 1

考虑以下JSON结构:

代码语言:javascript
复制
{"k1":
  {"k2":
    [{"a": 3, "b": 4, "c": 2},
     {"a": 1, "b": 2, "c": 9}]},
 "irrelevant": "x"}

和Haskell数据类型:

代码语言:javascript
复制
data My = My Int Int

上面的JSON应该解析成My:[My]的列表,而两个Int应该分别取自JSON数组的"a“和"b”键:

代码语言:javascript
复制
[My 3 4, My 1 2]

诚然,我已经在它最简单的部分遇到了麻烦。

下面是我开始使用Aeson的方法:

代码语言:javascript
复制
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上:

代码语言:javascript
复制
decode sample :: Maybe Object
Just (Object (fromList [("irreleva...

这就像预期的那样工作,JSON被解析。然而,下一步,获取关键字"k1“处的对象不起作用:

代码语言:javascript
复制
:t (fromJust $ (decode sample :: Maybe Object)) .: "k1"
...
  :: FromJSON a => aeson-0.11.2.1:Data.Aeson.Types.Internal.Parser a

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

我走的路对吗?

EN

回答 2

Stack Overflow用户

发布于 2017-02-04 14:32:14

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

用类求解

通常,您为每个JSON类型创建一个Haskell数据类型,并编写实现解析器的FromJSON类。你不必这样做,但它确实减轻了你的精神负担,并与你在其他项目中可能观察到的东西保持一致。为此,让我们为您的元素创建几个类型My,并为这些元素的列表创建Mys

代码语言:javascript
复制
{-# 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记录中提取a-b-c对象的列表,并对这些对象运行My解析器,以仅获取ab值:

代码语言:javascript
复制
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条目,我们可以将其解析为My值的Vector

代码语言:javascript
复制
instance FromJSON My where
  parseJSON (Object v) = My <$> v .: "a" <*> v .: "b"
  parseJSON o = fail $ "Invalid type for My: " ++ show o

My数据只是将ab字段解析为Int。看吧:

代码语言:javascript
复制
> decode sample :: Maybe Mys
Just (Mys [My 3 4,My 1 2])

不带类

您询问了:t (fromJust $ (decode sample :: Maybe Object)) .: "k1",这只是询问.:类型的一种奇特方式

代码语言:javascript
复制
> :t (.:)
(.:) :: FromJSON a => Object -> Text -> Parser a

因此,正如您所说的,您提供了一个对象和文本来获取Parser。我不建议再次使用Parser monad --您只是在decode中有效地使用了它。简而言之,我会说不,你没有走在通往幸福的道路上。

如果您不打算按照设计使用API,那么就忘记组合符,直接使用数据类型。也就是说,大量的Value类型的case破坏。首先是k1,它是一个Object (只是一个HashMap),然后提取k2的值,它是一个Array ( Vector),最后对于Vector的每个元素,再次提取一个对象并在那里查找ab键。举个例子,我打算把它写出来,但是如果你不至少允许你自己有一个Maybe monad,那么它是非常丑陋的。

票数 0
EN

Stack Overflow用户

发布于 2017-02-04 20:46:28

教程'Aeson: the tutorial' by Artyom可能会对您有帮助,就像它对我有帮助一样。

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

从JSON结构的底部开始,向上递增,函数parseMy :: Value -> Parser My处理具有'a‘'b’键的对象以生成My (如果成功);中间辅助函数parseMyList'处理此类对象的数组以生成My列表;parseMyList处理具有键'k1‘的对象,依次生成具有键'k2’的对象,以生成My列表。

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

代码语言:javascript
复制
{-# 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"
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42031937

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档