2012-05-15 8 views
11

sto usando Data.Aeson per analizzare alcuni JSON in un tipo di record. Di tanto in tanto i dati viene aggiunto al JSON e questo spezza il codice come Aeson lamenta qualcosa per l'effetto di:Fault tolerant JSON parsing

previsto Oggetto con 21 coppie nome/valore, ma ha ottenuto 23 nome/valore

Preferisco davvero analizzare il JSON in modo tollerante ai guasti - Non mi interessa se più campi vengono aggiunti al JSON in un secondo momento, basta analizzare qualunque cosa tu possa! C'è un modo per raggiungere questa tolleranza ai guasti? Qui è il mio codice:

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

Vorrei aggiungere che sto usando da deriveJSON Data.Aeson.TH per generare il codice di analisi. Se scrivo manualmente il codice FromJSON è fault tolerant ma non vorrei farlo ...

risposta

6

Se si utilizza GHC 7.2 o 7.4, il nuovo supporto generici in aeson non controlla la presenza di campi aggiuntivi . Non sono sicuro se questo è di progettazione o meno, ma lo usiamo per lo stesso motivo.

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

L'esecuzione di questo fallirebbe con il TH derivato esempio a causa di 'field2' non esistente nel record. L'istanza Generic restituisce il risultato desiderato:

Just (MyRecord {field1 = 1})