aeson
aeson copied to clipboard
Could not deduce `RecordFromJSON'` and `RecordToPairs` in `aeson-2.2.0.0`
I have the following code
{-# LANGUAGE DeriveGeneric #-}
import Data.Aeson
import GHC.Generics
data Item f a = Item { root :: a, children :: f a } deriving Generic1
instance FromJSON1 f => FromJSON1 (Item f) where liftParseJSON = genericLiftParseJSON defaultOptions
instance ToJSON1 f => ToJSON1 (Item f) where liftToJSON = genericLiftToJSON defaultOptions
instance (FromJSON1 f, FromJSON a) => FromJSON (Item f a) where parseJSON = parseJSON1
instance (ToJSON1 f, ToJSON a) => ToJSON (Item f a) where toJSON = toJSON1
data Content f a = Content { items :: f (Item f a) } deriving Generic1
instance (Functor f, FromJSON1 f) => FromJSON1 (Content f) where liftParseJSON = genericLiftParseJSON defaultOptions
instance (Functor f, ToJSON1 f) => ToJSON1 (Content f) where liftToJSON = genericLiftToJSON defaultOptions
instance (Functor f, FromJSON1 f, FromJSON a) => FromJSON (Content f a) where parseJSON = parseJSON1
instance (Functor f, ToJSON1 f, ToJSON a) => ToJSON (Content f a) where toJSON = toJSON1
main :: IO ()
main = return ()
With aeson-2.1.2.1
, this code works perfectly fine. With aeson-2.2.0.0
, I get the following errors:
Main.hs:15:82: error:
• Could not deduce (aeson-2.2.0.0:Data.Aeson.Types.FromJSON.RecordFromJSON'
One (f :.: Rec1 (Item f)))
arising from a use of ‘genericLiftParseJSON’
from the context: (Functor f, FromJSON1 f)
bound by the instance declaration at Main.hs:15:10-58
• In the expression: genericLiftParseJSON defaultOptions
In an equation for ‘liftParseJSON’:
liftParseJSON = genericLiftParseJSON defaultOptions
In the instance declaration for ‘FromJSON1 (Content f)’
|
15 | instance (Functor f, FromJSON1 f) => FromJSON1 (Content f) where liftParseJSON = genericLiftParseJSON defaultOptions
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Main.hs:16:10: error:
• Could not deduce (aeson-2.2.0.0:Data.Aeson.Types.ToJSON.RecordToPairs
(Data.Aeson.Encoding.Internal.Encoding' Value)
Series
One
(S1
('MetaSel
('Just "items")
'NoSourceUnpackedness
'NoSourceStrictness
'DecidedLazy)
(f :.: Rec1 (Item f))))
arising from a use of ‘aeson-2.2.0.0:Data.Aeson.Types.ToJSON.$dmliftToEncoding’
from the context: (Functor f, ToJSON1 f)
bound by the instance declaration at Main.hs:16:10-54
• In the expression:
aeson-2.2.0.0:Data.Aeson.Types.ToJSON.$dmliftToEncoding
@(Content f)
In an equation for ‘liftToEncoding’:
liftToEncoding
= aeson-2.2.0.0:Data.Aeson.Types.ToJSON.$dmliftToEncoding
@(Content f)
In the instance declaration for ‘ToJSON1 (Content f)’
|
16 | instance (Functor f, ToJSON1 f) => ToJSON1 (Content f) where liftToJSON = genericLiftToJSON defaultOptions
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Main.hs:16:75: error:
• Could not deduce (aeson-2.2.0.0:Data.Aeson.Types.ToJSON.RecordToPairs
Value
(dlist-1.0:Data.DList.Internal.DList
aeson-2.2.0.0:Data.Aeson.Types.Internal.Pair)
One
(S1
('MetaSel
('Just "items")
'NoSourceUnpackedness
'NoSourceStrictness
'DecidedLazy)
(f :.: Rec1 (Item f))))
arising from a use of ‘genericLiftToJSON’
from the context: (Functor f, ToJSON1 f)
bound by the instance declaration at Main.hs:16:10-54
• In the expression: genericLiftToJSON defaultOptions
In an equation for ‘liftToJSON’:
liftToJSON = genericLiftToJSON defaultOptions
In the instance declaration for ‘ToJSON1 (Content f)’
|
16 | instance (Functor f, ToJSON1 f) => ToJSON1 (Content f) where liftToJSON = genericLiftToJSON defaultOptions
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I looked at the changelog, and while there have apparently been some changes to the handling of Generics related to omitted fields, I did not find anything that would indicate that my situation should no longer work. Is this a regression or am I doing something wrong?
The behavior has changed.
If encoded as record, encoding Content Maybe a
should omit nothing fields, but such composition (i.e. instances for :.:
) are not there anymore. The changed instance is for ToJSON
case
instance ( Selector s
- , GToJSON' enc arity a
+ , GToJSON' enc arity (K1 i t)
, KeyValuePair enc pairs
- ) => RecordToPairs enc pairs arity (S1 s a)
+ , ToJSON t
+ ) => RecordToPairs enc pairs arity (S1 s (K1 i t))
where
- recordToPairs = fieldToPair
+ recordToPairs opts targs m1
+ | omitNothingFields opts
+ , omitField (unK1 $ unM1 m1 :: t)
+ = mempty
+
+ | otherwise =
+ let key = Key.fromString $ fieldLabelModifier opts (selName m1)
+ value = gToJSON opts targs (unM1 m1)
+ in key `pair` value
and two other RecordToPairs
instanecs for Rec1
and Par1
but not :.:
one.
Note, that we need to do omitNothingFields
checks on the value inside S1 s
now
Probably we'd need to add one more auxiliary class (say FieldToPairs
) and figure out how to write RecordToPairs enc pairs arity (S1 s x)
instance using FieldToPairs ... x
. And similarly for FromJSON
.
I have no idea how to do that, but as long as above test case is added as an regression test, and nothing else in test-suite needs to be changed I'll be happy to review the patch.
Alright, I understand. Unfortunately, I currently have neither the necessary insight and experience, nor the time to work on a patch for this. I will make do with the basic FromJSON
and ToJSON
instances for now.