2013-08-23 6 views
10

Ich versuche, den folgenden JSON mit AESON zu analysieren.Parse JSON mit Feldnamen, die reservierte Schlüsselwörter enthalten

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

Da es eine Menge von Feld sind würde ich ignorieren möchte, so scheint es I should use GHC generics. Aber wie schreibt man eine Datentyp-Definition, die Haskell-Schlüsselwörter wie data und type verwendet? Im Folgenden natürlich gibt: 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) 

Antwort

13

Sie können, ohne sich auf GHC.Generics Ihre eigene FromJSON und ToJSON Instanzen schreiben. Dies bedeutet auch, dass Sie unterschiedliche Feldnamen für die Datendarstellung und die JSON-Darstellung verwenden können.

Beispiel Instanzen für Beitrag:

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

Das gleiche gilt für Feed erfolgen. Wenn Sie Felder nicht ignorieren müssen, können Sie auch deriveJSON von Data.Aeson.TH verwenden, die eine Funktion zum Ändern von Feldnamen als erstes Argument verwendet.

+0

Danke, funktioniert gut! Wäre es auch möglich, diesen Ansatz mit 'Generic' zu kombinieren? Nehmen wir an, der 'Post'-Typ hatte nicht das' type'-Attribut, es scheint, dass ich 'Post derivating (Generic)' nicht haben kann, wenn 'Feed's' parseJSON' manuell implementiert und dann wie in der Frage kombiniert wird . – mb21

+0

Schließlich fragte ich mich, ob ich wirklich den 'Feed'-Datentyp brauche, nur um das' data'-Attribut im JSON loszuwerden oder ob ich irgendwie direkt an die Posts gelangen kann. – mb21

+1

@ mb21 [a] hat FromJSON/ToJSON-Instanzen. Wenn Sie also nur eine Liste von Posts serialisieren möchten, tun Sie dies direkt mit 'encode listOfPosts'. Und Sie können den Ansatz mit Generic kombinieren, leiten Sie die Instanz für Post einfach wie jede andere Instanz mit Generic ab und schreiben Sie die Instanz für Feed manuell. Es funktioniert einfach. – bennofs