2015-04-13 6 views
2

Я работал над небольшой библиотекой против API на основе JSON. Эта библиотека позволяет использовать в «опции» объекта, серия пар ключ-значение, которые определяют современное поведение:Анализ объекта опций для списка опций

{ 
    "id": 1234 
    ... 
    "options": { 
    "notify_no_data": True, 
    "no_data_timeframe": 20, 
    "notify_audit": False, 
    "silenced": {"*" :1428937807} 
    } 
} 

Я представлял концепцию опций в Haskell, используя список опций:

data Option = NotifyNoData NominalDiffTime -- notify after xyz 
      | NotifyAudit -- notify on changes 
      | Silenced (Maybe UTCTime) -- silence all notifications ("*") until xyz (or indefinitely) 

newtype Options = Options [Option] 

Реализация toJSON была достаточно простой; Я создал экземпляр toJSON для типа Option, а затем использовал их как помощники для типа Options.

instance ToJSON Option where 
    toJSON (Silenced mtime) = 
    Object $ Data.HashMap.fromList [("silenced", mapping)] 
    where stamp = maybe Null (jsonTime . floor . utcTimeToPOSIXSeconds) mtime 
      mapping = Object $ Data.HashMap.singleton "*" stamp 
    toJSON (NotifyNoData difftime) = 
    Object $ Data.HashMap.fromList [("notify_no_data", Bool True) 
            ,("no_data_timeframe", stamp)] 
    where stamp = jsonTime $ floor (difftime/60) 
    toJSON NotifyAudit = 
    Object $ Data.HashMap.fromList [("notify_audit", Bool True)] 

instance ToJSON Options where 
    toJSON (Options options) = Object $ Data.HashMap.unions $ reverse $ (opts:) $ map ((\(Object o) -> o) . toJSON) options 
    where opts = Data.HashMap.fromList [("silenced", Object Data.HashMap.empty) 
             ,("notify_no_data", Bool False) 
             ,("notify_audit", Bool False)] 

Проблема Я бегу в в fromJSON реализации для Options. Все варианты использования, которые я видел перед поставкой довольно простого json-object-представления для сопоставления представления данных. Что мне нужно сделать, так это преобразовать объект в объект опций в список представлений данных (Option). Например, образец JSON в разделе «варианты», которые я дал в начале бы стать:

Options [NotifyNoData 20, Silenced (Just (posixSecondsToUTCTime 1428937807))] 

FromJSON требует осуществления parseJSON :: FromJSON a => Value -> Parser a. У меня возникли проблемы с пониманием того, как создать Parser, используя эту необязательную структуру объектов, предоставленную API. Существует ли стандартный подход к синтаксическому анализу объекта JSON для такого списка? Возможно, я не в полной мере понимаю класс Parser.

ответ

4

Вы можете, возможно, использовать парсер монаду, чтобы извлечь всю информацию, что-то вроде:

parseJSON (Object v) = do 
     maybeNotify <- v .:? "notify_no_data" 
     maybeTimeFrame <- v .:? "no_data_timeframe" 
     let nots = case (maybeNotify,maybeTimeFrame) of 
        (Just True,Just stamp) -> [NotifyNoData $ fromJsontime stamp] 
        _ -> [] 
     return $ Options nots 

(fromJsontime просто вспомогательная функция для преобразования значения JSON вашего NominalDiffTime, я полагаю, у вас есть что-то для что).

Сделайте то же самое для других типов опций, объединив результат.

1

Используя json-stream анализатор (который я являюсь автором), это будет выглядеть следующим образом (из моей головы, непроверенные):

option =  NotifyNoData  <$> "no_data_timeframe" .: value 
           <* filterI id ("notify_no_data" .: bool) 
     <|> const NotifyAudit <$> filterI id ("notify_audit" .: bool) 
     <|> Silenced   <$> "silenced" .: "*" .:? value 

optionList = Options <$> toList option 

код ожидает Вас есть FromJSON экземпляр для UTCTime (или вы могли бы просто используйте «целое число» и какой-то метод fromposixtime и т. д.).