我有一个类似的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) = ???
我尝试使用Lens来破坏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’
有更好的方法吗?
是否有人将一组值解析为更详细的结构?
答案 0 :(得分:14)
Aeson有列表的实例,所以我认为没有必要处理向量。
{-# 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
答案 1 :(得分:4)
这里的诀窍是让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
如果无法为Int
和x
提取两个y
,则无法彻底解析。然后你只需要为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
从我原来的答案中大大简化了。感谢Max。