Как проанализировать значение JSON с помощью Aeson, который может быть одного из двух разных типов - PullRequest
0 голосов
/ 17 февраля 2019

В настоящее время я пытаюсь проанализировать некоторые данные JSON с помощью библиотеки aeson.Существует ряд свойств, которые имеют значение false, когда данные для этого свойства отсутствуют.Таким образом, если значение свойства обычно представляет собой массив целых чисел и для этого свойства нет данных, вместо предоставления пустого массива или null, значение равно false.(То, как эти данные структурированы, не является моей задачей, поэтому мне придется как-то с ними работать.)

В идеале я хотел бы получить пустой список в тех случаях, когда значение равнологическое значение.Я создал небольшой тестовый пример ниже для демонстрации.Поскольку мой конструктор данных Group ожидает список, он не может выполнить синтаксический анализ при обнаружении false.

  data Group = Group [Int] deriving (Eq, Show)

  jsonData1 :: ByteString
  jsonData1 = [r|
    {
      "group" : [1, 2, 4]
    }
  |]

  jsonData2 :: ByteString
  jsonData2 = [r|
    {
      "group" : false
    }
  |]

  instance FromJSON Group where
    parseJSON = withObject "group" $ \g -> do
      items <- g .:? "group" .!= []
      return $ Group items

  test1 :: Either String Group
  test1 = eitherDecode jsonData1
  -- returns "Right (Group [1,2,4])"

  test2 :: Either String Group
  test2 = eitherDecode jsonData2
  -- returns "Left \"Error in $.group: expected [a], encountered Boolean\""

. Сначала я надеялся, что оператор (.!=) по умолчанию разрешит ему пустой список.но это работает, только если свойство полностью отсутствует или null.Если бы это был "group": null, он бы успешно проанализировал, и я бы получил Right (Group []).

Любой совет, как заставить его успешно анализировать и возвращать пустой список в тех случаях, когда это false?

1 Ответ

0 голосов
/ 17 февраля 2019

Одним из способов решения этой проблемы является сопоставление с шаблоном для конструкторов данных JSON , которые действительны для вашего набора данных, и выдают недопустимые для всех остальных.

Например, вы можете написать что-то подобное для этого конкретного поля, помня, что parseJSON - это функция из Value -> Parser a:

instance FromJSON Group where
    parseJSON (Bool False) = Group <$> pure []
    parseJSON (Array arr) =  pure (Group $ parseListOfInt arr)
    parseJSON invalid    = typeMismatch "Group" invalid

parseListOfInt :: Vector Value -> [Int]
parseListOfInt = undefined -- build this function

Вы можете увидеть примерэто в Aeson docs , которые довольно хороши (но вы вроде как должны прочитать их несколько раз).

Я бы тогда, вероятно, определил отдельный запись для представления объекта верхнего уровня, в который входит этот ключ, и который полагается на общее извлечение, но у других может быть лучшее предложение там:

data GroupObj = GroupObj { group :: Group } deriving (Eq, Show)
instance FromJSON GroupObj

Одна вещь, которую всегда следует иметь в виду при работе с Aesonэто базовые конструкторы (из которых только 6) и базовые структуры данных (например, HashMap для Object и Vector для Array).

Например, в приведенном выше примере, когда вы сопоставляете шаблон с Array arr, вы должны знать, что вы получаете Vector Value в arr, и у нас еще есть работа, чтобыпревратить это в список целых чисел, поэтому я оставил эту другую функцию parseListOfInt выше, потому что я думаю, что это, вероятно, хорошее упражнение для ее построения?

...