2014-07-14 54 views
9

我有一個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) = ??? 

我用鏡頭來破壞數據點紀錄試過,但它不會編譯:

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

[下面是一個例子(https://gist.github.com/bheklilr/98ac8f8e663cf02fcaa6),我前一段時間寫了別人,可能會給你一個良好的開端。 – bheklilr

+0

感謝bheklilr,但是我遇到的問題並不是Aeson的解析(對象解析很簡單),而是專注於將數組解析爲更多語義數據類型。該數組具有'[X,Y]',它們是兩個不同的語義含義,僅由索引指示。我想將其解析爲一個真正的數據類型'DataPoint Int Int',我可以將這些類型和名稱精確到它應該的含義。 – cschneid

回答

14

艾森有列表實例,所以我認爲沒有必要處理向量。

{-# 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 
+0

我不得不承認,我更喜歡這個答案,我甚至不認爲列表可能已經有一個實例。 – bheklilr

+0

我也很驚訝地看到它。 –

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出去了用於xy。然後你只需要定義實例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。

+0

感謝您提前回答。讓我度過了我的障礙,並且最終使用了您的答案和@max taldykin的組合。 – cschneid