首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何用Haskell Aeson解析数组

如何用Haskell Aeson解析数组
EN

Stack Overflow用户
提问于 2014-07-14 18:02:22
回答 2查看 4.9K关注 0票数 10

我有一个JSON文档,它看起来像:

代码语言:javascript
复制
{ "series": [[1,2], [2,3], [3,4]] }

我想将其解析为一组数据类型:

代码语言:javascript
复制
data Series = Series [DataPoint]
data DataPoint = DataPoint Int Int  -- x and y

我在为FromJSON编写DataPoint实例时遇到了很多问题。

代码语言:javascript
复制
instance FromJSON DataPoint where
  parseJSON (Array a) = ???

我尝试过使用Lens来销毁DataPoint记录,但它没有编译:

代码语言:javascript
复制
case a ^.. values . _Integer of -}
  [x,y] -> DataPoint <$> x <*> y
  _     -> mzero

这个错误失败了(前两行我甚至没有镜头欺骗,只是试图创建一个DataPoint <$> 1 <*> 2):

代码语言:javascript
复制
Couldn't match type ‘aeson-0.7.0.6:Data.Aeson.Types.Internal.Parser
                       Integer’
              with ‘Integer’
Expected type: (aeson-0.7.0.6:Data.Aeson.Types.Internal.Parser
                  Integer
                -> Const
                     (Data.Monoid.Endo
                        [aeson-0.7.0.6:Data.Aeson.Types.Internal.Parse
                     (aeson-0.7.0.6:Data.Aeson.Types.Internal.Parser I
               -> Value
               -> Const
                    (Data.Monoid.Endo
                       [aeson-0.7.0.6:Data.Aeson.Types.Internal.Parser
                    Value
  Actual type: (Integer
                -> Const
                     (Data.Monoid.Endo
                        [aeson-0.7.0.6:Data.Aeson.Types.Internal.Parse
                     Integer)
               -> Value
               -> Const
                    (Data.Monoid.Endo
                       [aeson-0.7.0.6:Data.Aeson.Types.Internal.Parser
                    Value
In the second argument of ‘(.)’, namely ‘_Integer’
In the second argument of ‘(^..)’, namely ‘values . _Integer’

有更好的方法吗?

是否有将值数组解析为更详细的结构的示例?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-07-14 18:33:30

伊索有列表的例子,所以我认为没有必要处理向量。

代码语言:javascript
复制
{-# LANGUAGE LambdaCase #-}
import Data.Aeson

data Series = Series [DataPoint]
data DataPoint = DataPoint Int Int

instance FromJSON DataPoint where
  parseJSON jsn = do
    [x,y] <- parseJSON jsn
    return $ DataPoint x y

instance FromJSON Series where
  parseJSON = \case
    Object o -> (o .: "series") >>= fmap Series . parseJSON
    x -> fail $ "unexpected json: " ++ show x
票数 16
EN

Stack Overflow用户

发布于 2014-07-14 18:27:39

这里的诀窍是使FromJSON DataPoint的实例正确,这需要一些匹配,但并不太糟糕。我想出了

代码语言:javascript
复制
instance FromJSON DataPoint where
    parseJSON (Array v)
        | V.length v == 2 = do
            x <- parseJSON $ v V.! 0
            y <- parseJSON $ v V.! 1
            return $ DataPoint x y
        | otherwise = mzero
    parseJSON _ = mzero

如果它不能为xy提取出两个x,那么它将无法进行干净的解析。然后,只需定义Series的实例即可。

代码语言:javascript
复制
instance FromJSON Series where
    parseJSON (Object o) = do
        pts <- o .: "series"
        ptsList <- mapM parseJSON $ V.toList pts
        return $ Series ptsList
    parseJSON _ = mzero

再一次,如果数据在任何地方都有错误,它就会彻底失败。测试:

代码语言:javascript
复制
> decode "{\"series\": [[1, 2], [3, 4]]}" :: Maybe Series
Just (Series [DataPoint 1 2, DataPoint 3 4])
> decode "{\"series\": [[1, 2], [3, {}]]}" :: Maybe Series
Nothing

所以看起来很管用。

编辑:正如@maxtaldykin所指出的那样,您可以利用实例

代码语言:javascript
复制
instance FromJSON DataPoint where
    parseJSON obj = do
        [x, y] <- parseJSON obj
        return $ DataPoint x y

instance FromJSON Series where
    parseJSON (Object o) = do
        pts <- o .: "series"
        fmap Series $ parseJSON pts
    parseJSON _ = mzero

这比我原来的答案要简单得多。向麦克斯致敬。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24742872

复制
相关文章

相似问题

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