2015-02-13 6 views
4

Мой тип и коррекция реализации FromJSON, как указано ниже.Как правильно вывести ошибку в анализе JSON с помощью Data.Aeson

nonEmpty поворачивает List в Maybe NonEmpty, и я пытаюсь правильно иметь дело со случаем, когда List действительно пуст, и я должен прервать разбор. Этот синтаксический анализ фактически выполняется внутри parseJsonBody, что означает, что я не хочу error "foo" мой выход из него, но я хочу вернуть mzero (или что-то еще сделает трюк, mzero - единственное, на что я наткнулся, далеко), чтобы обработчик правильно возвращал 400 вместо аварийного с 500.

Подход ниже компилируется, но, насколько я могу судить, в значительной степени равен error или какой-либо другой форме исключения, бросающей внутрь parseJSON , Однако, если я возвращаю mzero (например, с помощью <*> mzero вместо этой строки), это не так хорошо, как предполагалось.

import qualified Data.List.NonEmpty as NE 
data GSAnswer = GSAnswer { gsAnswerQuestionId :: Int 
         , gsAnswerResponses :: NE.NonEmpty GSResponse 
         } deriving (Show, Eq) 


instance FromJSON GSAnswer where 
    parseJSON (Object o) = 
    GSAnswer <$> o .: "question-id" 
      -- how do I return mzero here based on NE.nonEmpty? 
      -- this will throw an exception right now on an empty list 
      <*> fmap (fromMaybe (fail "foo") . NE.nonEmpty) (o .: "responses") 
    parseJSON _ = mzero 

Одним из вариантов было бы как-то шаблон матча на результат fmap NE.nonEmpty (o .: "responses"), но я не могу вполне понять, что картина будет там: не выглядит как Parser имеет никаких конструкторов?

ответ

3

По сути, вам нужен Parser [a] -> Parser NE.NonEmpty трансформатор, который относительно легко:

-- toNonEmptyP :: Parser [a] -> Parser NE.NonEmtpy 
toNonEmptyP p = fmap NE.nonEmpty p >>= maybe mzero return 

отобразим NE.nonEmpty на нашем регулярном списке синтаксического анализа, который дает нам Parser (Maybe NE.NonEmpty). Затем мы проверяем Maybe на maybe и либо используем mzero, если это было Nothing, или return проанализированное значение обратно в контекст синтаксического анализа. Ваш FromJSON экземпляр затем сводится к

instance FromJSON GSAnswer where 
    parseJSON (Object o) = 
    GSAnswer <$> o .: "question-id" 
      <*> toNonEmptyP (o .: "responses") 
    parseJSON _ = mzero 

Вы можете использовать fail msg вместо mzero, чтобы обеспечить сообщение об ошибке пользовательской, так как fail :: String -> Parser a не Дно из.

+0

Кажется, сделать трюк, спасибо. Любые предложения относительно того, как добавить конкретное сообщение об ошибке к сбою Parser? В настоящее время сообщенное сообщение об ошибке «mzero», было бы аккуратно, если бы я мог передать свое собственное сообщение об ошибке, потому что Parser пришлось прервать. –

+0

Вы пробовали 'fail 'msg" 'вместо' mzero' в трансформаторе? Я сейчас не на своем ПК, но я могу проверить позже. – Zeta

+0

Это похоже на трюк, спасибо, я изменил приведенное выше: toNonEmptyP :: String -> Parser [a] -> Parser (NE.NonEmpty a) toNonEmptyP msg p = fmap NE.nonEmpty p >> = возможно (сбой msg) return –