2012-05-15 24 views
11

Ich verwende Data.Aeson, um einige JSON in einen Datensatztyp zu analysieren. Von Zeit zu Zeit werden Daten in den JSON hinzugefügt und das bricht meinen Code als Aeson etwas zum Effekt von klagt:Fehlertoleranter JSON-Parsing

erwartet Objekt mit 21 Namen/Wert-Paaren, aber bekam 23 Namen/Wert

Ich würde es wirklich vorziehen, den JSON fehlertolerant zu analysieren - es ist mir egal, ob dem JSON zu einem späteren Zeitpunkt weitere Felder hinzugefügt werden, parse einfach, was immer du kannst! Gibt es eine Möglichkeit, diese Fehlertoleranz zu erreichen? Hier ist mein Code:

myRecordFromJSONString :: BS.ByteString -> Maybe MyRecord 
myRecordFromJSONString s = case Data.Attoparsec.parse json s of 
    Done _rest res -> Data.Aeson.Types.parseMaybe parseJSON res 
    _    -> Nothing 

Ich sollte hinzufügen, dass ich deriveJSON von Data.Aeson.TH bin mit dem Parsing-Code zu generieren. Wenn ich den FromJSON-Code manuell schreibe, ist er fehlertolerant, aber ich möchte das nicht tun ...

Antwort

6

Wenn Sie GHC 7.2 oder 7.4 verwenden, sucht die neue Generics-Unterstützung in aeson nicht nach zusätzlichen Feldern . Ich bin mir nicht sicher, ob dies beabsichtigt oder nicht, aber wir verwenden es aus dem gleichen Grund.

{-# LANGUAGE DeriveGeneriC#-} 
{-# LANGUAGE OverloadedStrings #-} 

import Data.Aeson 
import qualified Data.Aeson.Types 
import Data.Attoparsec 
import qualified Data.ByteString as BS 
import Data.ByteString.Char8() 
import GHC.Generics 

data MyRecord = MyRecord 
    { field1 :: Int 
    } deriving (Generic, Show) 

instance FromJSON MyRecord 

myRecordFromJSONString :: BS.ByteString -> Maybe MyRecord 
myRecordFromJSONString s = case Data.Attoparsec.parse json s of 
    Done _rest res -> Data.Aeson.Types.parseMaybe parseJSON res 
    _    -> Nothing 

main :: IO() 
main = do 
    let parsed = myRecordFromJSONString "{ \"field1\": 1, \"field2\": 2 }" 
    print parsed 

Das Ausführen würde mit der abgeleiteten TH-Instanz fehlschlagen, da 'field2' nicht im Datensatz vorhanden ist. Die Generic Instanz gibt das gewünschte Ergebnis zurück:

Just (MyRecord {field1 = 1})