Haskell Aeson处理缺失的数据

时间:2015-11-25 08:52:52

标签: haskell aeson

我有一个(有效的)json编码数组,它有丢失或格式错误的数据。我希望Aeson将其转换为Maybe [Maybe Point]并使Nothing数组元素不是有效的Point

import Data.Aeson
decode "[1,2,3,4,null,\"hello\"]" :: (Maybe [Maybe Int])
=> Nothing

但我宁愿让它评估

=> Just [Just 1, Just 2, Just 3, Just 4, Nothing, Nothing]

如果不能用Aeson完成,是否有其他库可以做到?

请注意,实际对象比简单整数复杂得多,因此字符串操作不是一个可行的解决方案。

3 个答案:

答案 0 :(得分:4)

我们可以在Maybe周围创建一个新类型,在解析失败时不会失败,而是以Nothing成功:

{-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving #-}

import Data.Aeson
import Data.Coerce
import Control.Applicative
import Control.Monad

newtype Maybe' a = Maybe' (Maybe a) deriving
  (Eq, Ord, Show, Functor, Applicative, Monad, Alternative, MonadPlus)

instance FromJSON a => FromJSON (Maybe' a) where
  parseJSON v = do
    case fromJSON v :: Result a of
      Success a -> pure (Maybe' $ Just a)
      _         -> pure (Maybe' $ Nothing)

现在我们可以使用Maybe'包装类型以获得所需的行为:

> decode "[4, 5, \"foo\", \"bar\"]" :: Maybe [Maybe' Int]
Just [Maybe' (Just 4),Maybe' (Just 5),Maybe' Nothing,Maybe' Nothing]

但是,我们之后很有可能希望使用常规Maybe值。 Data.Coerce在这里很方便,因为它允许我们将Maybe' - s强制转换为Maybe - s,无论它们在结果类型中的位置如何:

> coerce (decode "[[[], 3], [4, 5]]" :: Maybe [(Maybe' Int, Int)]) :: Maybe [(Maybe Int, Int)]
Just [(Nothing,3),(Just 4,5)]

答案 1 :(得分:2)

我会使用值并从中工作:

> let fromNum v = case v of Number x -> Just x ; _ -> Nothing 
> maybe [] (map fromNum) (decode  "[1,2,3,4,null,\"hello\"]" :: (Maybe [Value])
[Just 1.0,Just 2.0,Just 3.0,Just 4.0,Nothing,Nothing]

所以

> let tmp = maybe [] (map fromNum) (decode  "[1,2,3,4,null,\"hello\"]" :: (Maybe [Value])
> let keepJust l v = case v of Just i -> i:l; Nothing -> l
> reverse (foldl keepJust [] tmp)
[1.0,2.0,3.0,4.0]

非常无效(但安全)的解决方案是:

Vector

如果您想提高效率,可以使用foldl'和{{1}}。

答案 2 :(得分:1)

首先,您的预期结果不是(Maybe [Maybe Int])类型,而是[Maybe Int]类型。

其次,截至decode的定义,这是不可能的。但是,您可以使用decode将JSON数组解码为Aeson值列表,然后您可以映射并解码实际值。

因此,单个值的解析器错误不会导致整个数组的解析器错误。

编辑:当您修改期望时,这可能会对您有所帮助:

Prelude Data.ByteString.Lazy.Char8 Data.Aeson> (decode (pack "[1,2,3,null,\"hello\"]") :: Maybe [Value])
Just [Number 1.0,Number 2.0,Number 3.0,Null,String "hello"]

从那里,您可以映射和匹配,以满足您的需求(因为只有圆形浮动可能不适合)。