Отказоустойчивый анализ JSON

Я использую Data.Aseon для разбора некоторого JSON в тип записи. Время от времени данные добавляются в JSON, и это прерывает мой код, поскольку Aeson жалуется на эффект:

ожидаемый объект с 21 имя/значение, но получили 23 имени/значения

Я бы предпочел разобрать JSON отказоустойчивым способом - мне все равно, добавлено ли больше полей в JSON позже, просто проанализируйте все, что вы можете! Есть ли способ достичь этой отказоустойчивости? Здесь мой код:

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

Я должен добавить, что я использую deriveJSON из Data.Aeson.TH для генерации кода синтаксического анализа. Если я напишу код FromJSON вручную, он будет терпимым к ошибкам, но я бы не хотел этого делать...

Ответ 1

Если вы используете GHC 7.2 или 7.4, поддержка новых дженериков в aeson не проверяет наличие дополнительных полей. Я не уверен, что это по дизайну или нет, но мы используем его по той же причине.

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

Выполнение этого завершится неудачей с экземпляром TH из-за того, что в записи не было "field2". Экземпляр Generic возвращает желаемый результат:

Just (MyRecord {field1 = 1})