Пользовательский FromJSON для пользовательского типа

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

data Permission = Read | Write

Раньше общий вызов:

instance ToJSON Permission where 

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

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

Что имеет смысл, но ломает код, который я написал. Я написал часть toJSON вручную, чтобы получить правильно выглядящий материал, но написание fromJSON меня смущает.

Любые идеи?

Спасибо


person TallerGhostWalt    schedule 13.09.2013    source источник
comment
Я немного смущен тем, почему вы вообще хотите использовать экземпляры JSON для этого типа данных. Этот тип данных представляет собой константу в большей степени, чем абстрактный тип. Я мог видеть, что это используется как часть более крупной структуры данных, имеющей заданное разрешение, то есть {..., "permission" : "read"}. Хотите уточнить, как он используется?   -  person fredugolon    schedule 13.09.2013


Ответы (2)


Поскольку значение, содержащееся в конструкторе 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 для всех других входных данных.

person bheklilr    schedule 13.09.2013
comment
Спасибо, это выглядит лучше, чем то, что я сделал, и я изменю его. Кроме того, я просмотрел Data.Aeson и увидел использование ‹|› для обработки альтернатив, которые также хорошо работали. - person TallerGhostWalt; 14.09.2013
comment
@TallerGhostWalt Все, что подходит вам лучше всего. Я думаю, что любая реализация будет примерно одинаковой по скорости и, вероятно, будет довольно читабельной. Я, конечно, не эксперт по Aeson, так что, возможно, есть лучший способ сделать это, но я бы не стал слишком беспокоиться об этом. Однако вы можете сократить его с помощью всего лишь case keys v of, если хотите сохранить строку. Я просто предпочитаю не иметь выражения в случае, это всегда кажется мне неряшливым. - person bheklilr; 14.09.2013

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

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

data Permission = Read | Write

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

Взгляните на Optionsопределение< /a>, он содержит другие полезные поля.

person lambdas    schedule 09.10.2013