2013-08-23 7 views
10

Próbuję parsować następujący JSON z aesonem.Parsuj JSON z nazwami pól, które zawierają zastrzeżone słowa kluczowe

{ 
    "data": [ 
     { 
      "id": "34", 
      "type": "link", 
      "story": "foo" 
     }, 
     { 
      "id": "35", 
      "type": "link", 
      "story": "bar" 
     } 
    ] 
} 

Ponieważ istnieje wiele pola chciałabym zignorować, wydaje I should use GHC generics. Ale jak napisać definicję typu danych, która używa słów kluczowych Haskella, takich jak data i type? Poniższa oczywiście daje: parse error on input `data'

data Feed = Feed {data :: [Post]} 
    deriving (Show, Generic) 

data Post = Post { 
     id :: String, 
     type :: String, 
     story :: String 
    } 
    deriving (Show, Generic) 

Odpowiedz

13

Można pisać własne FromJSON i ToJSON instancje bez polegania na GHC.Generics. Oznacza to również, że można używać różnych nazw pól dla reprezentacji danych i reprezentacji JSON.

przykładowych na wiadomość:

{-# LANGUAGE OverloadedStrings #-} 
import Control.Applicative 
import Data.Aeson 
import qualified Data.ByteString.Lazy as LBS 

data Post = Post { 
     postId :: String, 
     typ :: String, 
     story :: String 
    } 
    deriving (Show) 

instance FromJSON Post where 
    parseJSON (Object x) = Post <$> x .: "id" <*> x.: "type" <*> x .: "story" 
    parseJSON _ = fail "Expected an Object" 

instance ToJSON Post where 
    toJSON post = object 
    [ "id" .= postId post 
    , "type" .= typ post 
    , "story" .= story post 
    ] 

main :: IO() 
main = do 
    print $ (decode $ Post "{\"type\": \"myType\", \"story\": \"Really interresting story\", \"id\" : \"SomeId\"}" :: Maybe Post) 
    LBS.putStrLn $ encode $ Post "myId" "myType" "Some other story" 

To samo może być wykonane w paszy. Jeśli nie musisz ignorować pól, możesz również użyć deriveJSON z Data.Aeson.TH, która przyjmuje funkcję do modyfikowania nazw pól jako pierwszego argumentu.

+0

Dzięki, działa ładnie! Czy byłoby również możliwe połączenie tego podejścia z "Generic"? Powiedzmy, że typ 'Post' nie miał atrybutu' type', wydaje się, że nie mogę mieć funkcji 'Post deriving (Generic)' podczas implementowania 'parseJSON'' Feed', a następnie łączyć je jak w pytaniu . – mb21

+0

W końcu zastanawiałem się, czy naprawdę potrzebuję typu danych "Feed", aby pozbyć się atrybutu "data" w JSON lub czy mogę w jakiś sposób dostać się bezpośrednio do Postów. – mb21

+1

@ mb21 [a] ma instancje FromJSON/ToJSON, więc jeśli wszystko, co chcesz zrobić, to serializować listę postów, po prostu zrób to bezpośrednio z 'encode listOfPosts'. Możesz łączyć podejście z Generic, wyprowadzać instancję postu jak każdą inną instancję używając Generic i pisać ręcznie instancję Feed. Po prostu działa. – bennofs