2011-07-26 46 views
13

Tôi cố gắng để phân tích cú pháp JSON có dạng sau đây sử dụng aesonLàm thế nào để phân tích cú pháp JSON lồng nhau với aeson

{"field":{"name":"..."}} 

or 

{"tag":{"name":"..."}} 

or 

{"line":{"number":"..."}} 

để xây dựng các kiểu dữ liệu sau

data Rule = Line Integer 
      | Field L.ByteString 
      | Tag L.ByteString 

Thật không may, tôi phải đối mặt với hai vấn đề mà tôi đã không tìm thấy giải pháp cho, cụ thể là:

  1. Làm cách nào để phân tích cú pháp JSON lồng nhau? Nhìn vào việc thực hiện (.:), nó sử dụng tra cứu để trích xuất giá trị của một khóa cụ thể. Tôi do dự để làm một cái gì đó như thế này vì nó có vẻ là dựa quá nhiều vào các chi tiết cụ thể về cách aeson thực hiện mọi thứ. Tôi có sai khi nghĩ đây là vấn đề không?

  2. Làm cách nào để sử dụng đúng hàm tạo dữ liệu dựa trên khóa nào có trong JSON? Tất cả những nỗ lực của tôi với < |> đã dẫn tôi đến hư không.

Tôi sẽ đăng mã mà tôi đã viết từ trước tới nay, nhưng tôi thậm chí còn chưa đến mức tôi có bất kỳ thứ gì đáng để đăng.

Trả lời

9

Làm cách nào sau đây?

{-# LANGUAGE OverloadedStrings #-} 

import Control.Applicative 
import   Data.Aeson 
import   Data.Aeson.Types 
import qualified Data.ByteString  as B 
import qualified Data.ByteString.Lazy as L 
import qualified Data.Map    as M 

data Rule = Line Integer 
      | Field L.ByteString 
      | Tag L.ByteString 
      deriving Show 

instance FromJSON Rule where 
    parseJSON j = do 
    o <- parseJSON j -- takes care of JSON type check 
    case M.toList (o :: Object) of 
     [("field", Object o')] -> Field <$> o' .: "name" 
     [("tag", Object o')] -> Tag <$> o' .: "name" 
     [("line", Object o')] -> Line <$> o' .: "number" 
     _      -> fail "Rule: unexpected format" 
+0

Cảm ơn rất nhiều, đây chính xác là những gì tôi đang tìm kiếm! Tôi đã thực hiện một thay đổi nhỏ cho hai dòng đầu tiên để làm cho nó trở thành 'parseJSON (Object o) = trường hợp M.toList o của', cũng như thêm một 'parseJSON _ = mzero' riêng biệt. –

+0

@ luke_randall, btw, tôi đã sử dụng 'o <- parseJSON j' thay vì' mzero' với mục đích, vì 'mzero' không cung cấp bất kỳ thông tin hữu ích nào về những gì đã xảy ra ngoài một' mzero '', trong khi 'parseJSON' cung cấp cho bạn các thông báo lỗi như' "khi mong đợi một Text Map a, gặp Array thay vì" ' – hvr

+0

Alright, điều đó có ý nghĩa. Cảm ơn bạn đã giải thích lý do - tôi nghĩ tôi sẽ hoàn nguyên. –

6

Đối với vấn đề này, tôi đã tạo ra một hàm mà nhìn lên một phím:

lookupE :: Value -> Text -> Either String Value 
lookupE (Object obj) key = case H.lookup key obj of 
     Nothing -> Left $ "key " ++ show key ++ " not present" 
     Just v -> Right v 
loopkupE _ _    = Left $ "not an object" 

và sử dụng nó hai chức năng mà tổ thành các đối tượng:

(.:*) :: (FromJSON a) => Value -> [Text] -> Parser a 
(.:*) value = parseJSON <=< foldM ((either fail return .) . lookupE) value 

(.:?*) :: (FromJSON a) => Value -> [Text] -> Parser (Maybe a) 
(.:?*) value = either (\_ -> return Nothing) (liftM Just . parseJSON) 
       . foldM lookupE value 
-- Or more simply using Control.Alternative.optional 
-- (.:?*) value keys = optional $ value .:* keys 

Chỉ lookupE phụ thuộc vào đại diện nội bộ để dễ dàng sửa đổi nó, nếu điều đó thay đổi. Sau đó, {"tag":{"name":"..."}} được phân tích cú pháp là v .:* ["tag", "name"]. Lưu ý rằng nó cũng hoạt động cho các danh sách trống - v .:* [] tương đương với parseJSON v.

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