2014-07-14 21 views
9

Tôi có một doc JSON trông giống như:Phân tích một Array với Haskell Aeson

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

Tôi muốn phân tích này vào một tập hợp các kiểu dữ liệu:

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

Tôi đang gặp rất nhiều sự cố khi cố gắng viết ví dụ FromJSON cho DataPoint.

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

Tôi đã cố gắng sử dụng thấu kính để hủy bản ghi Datapoint, nhưng nó không biên dịch:

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

Đó không thành công với lỗi này (hai dòng đầu tiên tôi nhận được thậm chí vắng mặt những thủ đoạn gian trá ống kính , chỉ cần cố gắng để tạo ra một 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’ 

có cách nào tốt hơn để làm điều này?

Có ai có ví dụ về phân tích mảng giá trị thành cấu trúc chi tiết hơn không?

+0

[Dưới đây là một ví dụ] (https://gist.github.com/bheklilr/98ac8f8e663cf02fcaa6) Tôi đã viết lên một lúc trước cho người khác, nó có thể cung cấp cho bạn một khởi đầu tốt. – bheklilr

+0

Cảm ơn bheklilr, nhưng vấn đề tôi đang gặp phải không phải là phân tích cú pháp của Aeson (Phân tích cú pháp đối tượng là dễ dàng), nhưng tập trung vào việc phá hủy một mảng thành một kiểu dữ liệu ngữ nghĩa hơn. Mảng có '[X, Y]', trong đó chúng có hai ý nghĩa ngữ nghĩa khác nhau, chỉ được biểu thị bằng chỉ mục. Tôi muốn phân tích cú pháp đó thành một kiểu dữ liệu thực 'DataPoint Int Int' để tôi có thể tinh chỉnh các loại và tên xuống chính xác những gì nó cần phải có ý nghĩa. – cschneid

Trả lời

14

Aeson có thể hiện danh sách, vì vậy tôi nghĩ không cần thiết phải xử lý vectơ.

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

Tôi phải thừa nhận rằng tôi thích câu trả lời này tốt hơn, tôi thậm chí không nghĩ rằng các danh sách có thể có một thể hiện rồi. – bheklilr

+0

Tôi cũng ngạc nhiên khi thấy nó. –

4

Bí quyết ở đây là nhận được bản sao cho số FromJSON DataPoint chính xác, có một chút phù hợp nhưng không quá tệ. Tôi đã đưa ra

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 

Mà sẽ thất bại trong việc phân tích cú pháp sạch nếu nó không có khả năng để kéo hai Int s ra cho xy. Sau đó, bạn chỉ cần xác định cá thể cho Series:

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

Mà, một lần nữa, sẽ xóa sạch nếu dữ liệu không đúng định dạng ở bất kỳ đâu. Để kiểm tra:

> decode "{\"series\": [[1, 2], [3, 4]]}" :: Maybe Series 
Just (Series [DataPoint 1 2, DataPoint 3 4]) 
> decode "{\"series\": [[1, 2], [3, {}]]}" :: Maybe Series 
Nothing 

Vì vậy, có vẻ như nó hoạt động.


EDIT: Như @maxtaldykin đã chỉ ra, bạn chỉ có thể tận dụng lợi thế của FromJSON a => FromJSON [a] dụ với

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 

nào được đơn giản hóa rất nhiều từ câu trả lời ban đầu của tôi. Kudos thành Max.

+0

Cảm ơn bạn đã trả lời nhanh trước đó.Đã cho tôi vượt qua rào cản của tôi, và tôi đã kết thúc bằng cách sử dụng kết hợp cả câu trả lời của bạn và @max taldykin. – cschneid

Các vấn đề liên quan