如何使用Haskell Aeson解析数组

0lvr5msh  于 2023-10-19  发布在  其他
关注(0)|答案(2)|浏览(101)

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

{ "series": [[1,2], [2,3], [3,4]] }

我想把它解析成一组数据类型:

data Series = Series [DataPoint]
data DataPoint = DataPoint Int Int  -- x and y

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

instance FromJSON DataPoint where
  parseJSON (Array a) = ???

我尝试使用透镜来销毁DataPoint记录,但它无法编译:

case a ^.. values . _Integer of -}
  [x,y] -> DataPoint <$> x <*> y
  _     -> mzero

这会因为这个错误而失败(我得到的前两行甚至没有透镜技巧,只是试图创建一个DataPoint <$> 1 <*> 2):

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’

”有更好的方法来做到这一点吗?**

有没有人有一个例子,将值的数组解析成更详细的结构?

tzcvj98z

tzcvj98z1#

Aeson有一个list的示例,所以我认为没有必要处理向量。

{-# 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
ny6fqffe

ny6fqffe2#

这里的技巧是使FromJSON DataPoint的示例正确,这需要一点匹配,但并不太糟糕。我想出了

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提取两个Int,那么它将无法干净地解析。然后你只需要定义Series的示例:

instance FromJSON Series where
    parseJSON (Object o) = do
        pts <- o .: "series"
        ptsList <- mapM parseJSON $ V.toList pts
        return $ Series ptsList
    parseJSON _ = mzero

同样,如果数据在任何地方格式不正确,它将完全失败。测试:

> 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所指出的,您可以使用FromJSON a => FromJSON [a]示例,

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

我的回答已经大大简化了。马克斯的荣誉

相关问题