2012-05-15 19 views
11

Estoy usando Data.Aeson para analizar algunos JSON en un tipo de registro. De vez en cuando se añaden datos a la JSON y esto rompe el código como Aesón se queja de algo en el sentido de:Análisis JSON tolerante a errores

espera de objetos con 21 pares nombre/valor, pero tiene 23 nombre/valor

Realmente preferiría analizar el JSON de una manera tolerante a fallas: no me importa si se agregan más campos al JSON en una fecha posterior, ¡simplemente analice todo lo que pueda! ¿Hay alguna forma de lograr esta tolerancia a fallas? Aquí está mi código:

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

debo añadir que estoy usando deriveJSON de Data.Aeson.TH para generar el código de análisis. Si escribo el código FromJSON manualmente, es tolerante a errores pero me gustaría no tener que hacer eso ...

Respuesta

6

Si está utilizando GHC 7.2 o 7.4, la nueva compatibilidad con genéricos en aeson no comprueba si hay campos adicionales . No estoy seguro de si esto es por diseño o no, pero lo usamos por la misma razón.

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

La ejecución de esta falla con la instancia TH derivada debido a que 'field2' no existe en el registro. La instancia Generic devuelve el resultado deseado:

Just (MyRecord {field1 = 1})