2013-09-13 6 views
1

Новейшая версия Data.Aeson изменила способ, которым ToJSON и FromJSON работа для простых типов, таких как:FromJSON на заказ для пользовательского типа

data Permission = Read | Write 

Он имел обыкновение быть, что общий вызов:

instance ToJSON Permission where 

... Создал бы JSON, который выглядел бы как {«Читать»: []} или {"Write": []}.

Но теперь она создает: {tag:"Read",contents:"[]"}

Который имеет смысл, но ломает код, который я написал. Я написал часть JSON вручную, чтобы дать правильный взгляд, но письмо от JSON меня сбивает с толку.

Любые идеи?

Благодаря

+1

Я немного смущен относительно того, почему вы хотели бы использовать/fromJSON экземпляры для этого типа данных в первую очередь. Этот тип данных представляет собой константу больше, чем абстрактный тип. Я мог видеть, что это используется как часть более крупной структуры данных, имеющей заданное разрешение, то есть «{...,« разрешение »:« читать »}'. Поразмыслить над тем, как он используется? – fredugolon

ответ

1

Поскольку значение, содержащееся в Object конструктор для Data.Aeson.Value является лишь строгим HashMap, мы можем извлечь ключи от него и принять решение, основываясь на этом. Я пробовал это, и он работал очень хорошо.

{-# LANGUAGE OverloadedStrings #-} 
module StackOverflow where 

import Data.Aeson 
import Control.Monad 
import Data.HashMap.Strict (keys) 

data Permission = Read | Write 

instance FromJSON Permission where 
    parseJSON (Object v) = 
     let ks = keys v 
     in case ks of 
      ["Read"] -> return Read 
      ["Write"] -> return Write 
      _ -> mzero 
    parseJSON _ = mzero 

Вы можете проверить его на decode "{\"Read\": []}" :: Maybe Permission. mzero в parseJSON гарантирует, что если что-то еще передано, оно просто вернет Nothing. Поскольку вы, похоже, хотите только проверить, есть ли один ключ, соответствующий одному из ваших двух разрешений, это довольно просто и правильно вернет Nothing на все остальные входы.

+0

Спасибо, это выглядит лучше, чем то, что я сделал, и я изменю его. Кроме того, я просмотрел Data.Aseon и увидел, используя <|> для обработки альтернатив, которые также хорошо работали. – TallerGhostWalt

+0

@TallerGhostWalt Все, что лучше всего подходит для вас. Я думаю, что либо реализация была бы приблизительно эквивалентной по скорости, и, вероятно, она была бы достаточно читаема. Я, конечно, не эксперт в Aeson, так что может быть лучший способ сделать это, но я бы не стал слишком беспокоиться об этом. Вы могли бы сократить его с помощью только 'case keys v of', хотя, если вы хотите сохранить строку. Я просто предпочитаю не иметь выражения в случае, мне всегда кажется беспорядочным. – bheklilr

2

Вы можете контролировать, как тип данных со всеми нулевыми конструкторами кодируется с использованием поля allNullaryToStringTag на Data.Aeson.Options. Установите его на True и он будет закодирован просто как строка.

import Data.Aeson.Types (Options (..), defaultOptions) 

data Permission = Read | Write 

$(deriveToJSON (defaultOptions {allNullaryToStringTag = True}) ''Permission) 

Посмотрите на Options definition, он содержит другие полезные поля.